You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The template library holds three kinds of template. Basecamp's own navigation separates them, and the CLI did not: templates list meant project templates, templates library meant to-do list templates, and nothing in either name said so. You found out by running one and reading what came back.
This groups the commands by the kind they act on, adds card table templates, and adds a way to turn work you already have into a template.
The flat spellings are hidden and deprecated, not removed. templates list, show, create, update, delete, construct and construction run templates projects …; templates library runs templates todolists list; templates copy and copy-status run templates todolists duplicate and duplication. They're out of --help and completion, so the grouped tree is the only one people discover, and each prints a one-line deprecation notice to stderr naming the new command. Output is otherwise unchanged. copy and copy-status also survive as aliases inside the grouped paths.
The 252 surface lines the grouped tree replaces are acknowledged in .surface-breaking (hidden commands aren't in .surface), and STYLE.md now says to hide and deprecate shipped spellings rather than remove them.
Taking a template out of the library
Basecamp offers to archive or delete a to-do list or card table template, and the CLI could do neither. templates show and templates delete route to project templates and answer 404 for anything in the library.
A library template is a recording, so archive, trash and restore go through the recordings status endpoint. They are spelled the way every other recording in this CLI is: cards, files, messages, comments and todolists all use trash/archive/restore off one shared helper, so delete here would have made the templates tree the only place the same operation on the same kind of object gets a different verb. templates projects delete keeps its name because it is a different endpoint.
restore has no UI equivalent under that name; it is the CLI spelling of the "unarchive it" affordance on an archived template.
Naming
Three asynchronous operations now read the same way, each status command named after the record it polls:
construct -> construction
duplicate -> duplication
templatify -> templatification
duplicate follows the product, which calls the operation Duplicate everywhere it appears. The API resource stays copies and the SDK operation stays CreateLibraryCopy; that separation is deliberate and matches what the front-end already does, where the UI says Duplicate and the route says copies.
templatify and templatification are coined words, and they are the only honest ones available. The product has no name for this operation at all, so rather than borrowing an unrelated English word that would read ambiguously next to todolists' other verbs, the CLI takes the resource noun. The help text carries the definition at the point you meet it.
A card table group
There was no home for actions on a card table itself. cards manages what is inside a board, and its --card-table flag only picks which board to look in. basecamp card-tables is that home, and it starts with one verb because a command group is a resource identity rather than a subcommand quota. messageboards has exactly one action for the same reason.
Duplicating names the project, not the container
duplicate sends the destination project and Basecamp resolves the container from the template's kind: a to-do list into the project's To-dos tool, a card table onto its dock. --todoset still pins the container when a project has more than one to-do set, and still checks that it belongs to the project and is enabled, because a clear message beats a 404. card-tables duplicate has no container flag, because a project has exactly one dock.
Verification
Unit tests cover request shape and output rendering. e2e covers error paths, flag surface and help.
Run against a live server with a seeded database:
templatify <id> with no flags sends {}. The template came back carrying the source's title, "Strategy ideas".
card-tables templatify on the same board twice, with and without --move-cards-to-triage. With it, all 7 cards including one under an on-hold container are in Kanban::Triage. Without it, Triage 1, Column 4, NotNowColumn 1, OnHold 1. The two results differ, so the flag reaches the wire rather than being dropped.
templatification on both kinds reports a real template name and id, and the matching destination_todolist / destination_card_table is populated, so neither falls through to the generic completion message.
templatification with its two ids transposed exits 2 with not_found, rather than reading a different record.
templatify on a recording that cannot be templatified exits 4 with forbidden, which is a different code and shape from the transposed case, so the two stay distinguishable.
templates todolists create, templates todolists list and templates card-tables list all succeed, and everything created during the run appears in the listings.
stderr was empty on every command, including both failures.
archive, trash and restore were not exercised against a live account from this branch. Unit tests pin each verb to its exact route (/:account/recordings/:id/status/{archived,trashed,active}.json) and assert a non-numeric id is rejected before any request goes out. The endpoints themselves were verified end to end against a local bc3 for both template kinds: each call returned 204 and the status transition was confirmed in the database.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The pinned SDK leaves the CLI uncompilable, and coverage, test, completion-breadcrumb, and documentation fixes remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR groups template commands by kind and adds card-table templates plus templatification workflows while preserving legacy aliases.
Changes:
Adds grouped project, to-do list, and card-table template commands.
Adds duplication and templatification operations, including card triage support.
Updates registrations, documentation, generated surfaces, tests, and smoke coverage.
File summaries
File
Reviewed changes and findings
skills/basecamp/SKILL.md
Documents grouped commands and aliases. Nit (1 vote, line 1022): document card-table compatibility aliases.
internal/commands/todolists.go
Registers to-do-list templatification commands.
internal/commands/templates.go
Implements template operations. Critical (3 votes, line 148): bump the SDK and refresh provenance/checksums; the current pin leaves the CLI uncompilable. Moderate (2 votes, line 1128): add card-table templatification tests. Moderate (1 vote, line 267): add card-table creation tests. Nit (1 vote, line 148): update API coverage. Moderate (1 vote, line 330): preserve the destination project in completion breadcrumbs.
internal/commands/templates_test.go
Adds request and output tests.
internal/commands/commands.go
Updates the command catalog. Nit (1 vote, line 46): update the stale API coverage matrix and summary.
internal/commands/commands_test.go
Updates command registration fixtures.
internal/commands/cardtables.go
Adds the card-tables command group.
internal/cli/root.go
Registers card-tables at the root.
e2e/templates.bats
Adds CLI surface and validation coverage. Nit (1 vote, line 359): rename the duplicated test case.
e2e/smoke/smoke_lifecycle.bats
Excludes new mutations from lifecycle smoke coverage.
e2e/smoke/smoke_account.bats
Adds account-level template smoke coverage.
.surface
Updates generated command and flag metadata.
Review details
Suppressed comments (6)
e2e/templates.bats:359
This test name is duplicated at lines 302 and 359, so the Bats report cannot distinguish the template-group help check from the root card-tables help check. Rename this newly added case to describe the root command explicitly.
@test "card-tables without subcommand shows help" {
internal/commands/commands.go:46
The new SDK-backed methods also leave the coverage matrix stale: API-COVERAGE.md still reports 10 template operations and 192/192 tracked endpoints. The repository's SDK completeness bar in AGENTS.md:160-164 requires a coverage row for every new SDK service method; update the matrix and summary alongside this catalog entry.
CreateLibraryCardTable is a new SDK operation, but the tests cover only card-table listing and duplication; unlike the to-do-list create path, there is no recording test for this endpoint's request body and rendered result. Add one alongside TestTemplatesTodolistsCreateSendsNameAndDescription so a wrong route or payload cannot pass.
The new template operations are not reflected in API-COVERAGE.md: its Templates row still lists 10 operations and omits card-table template reads/creation, to-do-list template creation, and templatification. The repository's SDK sync rule in AGENTS.md:146-164 requires an API-COVERAGE entry for every new SDK method, so update the matrix and totals with this change.
library, err := app.Account().Templates().GetLibraryCardTables(cmd.Context())
if err != nil {
internal/commands/templates.go:330
The completion breadcrumb drops the destination project. cards list always resolves a project before using --card-table (see internal/commands/cards.go:511-525), so without a configured project this follow-up prompts or can target the wrong project and fail to find the duplicated board. Include the destination bucket in the breadcrumb so the generated command is self-contained.
Cmd: fmt.Sprintf("basecamp cards list --card-table %d%s", table.ID, contextArgs),
skills/basecamp/SKILL.md:1023
The implementation exposes copy and copy-status as aliases under both grouped kinds, but this documentation only mentions templates todolists. Please mention templates card-tables here too so users can discover the compatibility spelling for card-table templates.
`copy` and `copy-status` also work inside `templates todolists`. Prefer the grouped,
canonical spellings above when writing new commands.
Files reviewed: 12/12 changed files
Comments generated: 2
Review effort level: Lite (auto)
Note
Copilot is running an experiment and ran this review at Lite.
thehale
changed the title
Group the template library by kind, and let people templatify work they already have
Todolist and Card Table Templates
Sep 12, 2026
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Legacy template command compatibility is not implemented, and card-table creation lacks required request/output coverage.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (6)
.surface-breaking:24
This allowlist records the legacy flat template commands as intentional removals, but the PR description explicitly promises that those spellings still resolve. Keeping these entries would let the surface check accept a compatibility break; either remove the allowlist entries and preserve the commands, or correct the compatibility promise everywhere.
This new style rule says every flat template spelling has been removed, which conflicts with the PR description's explicit backward-compatibility requirement. Keep the canonical grouped form as the style guidance, but document the legacy spellings as supported aliases if the stated contract is retained.
Where a group is split by the kind of thing it holds, every verb lives under the
kind it acts on. `templates` has `projects`, `todolists`, and `card-tables`; the
flat pre-grouping spellings (`templates list`, `templates show`, `templates
create`, `templates update`, `templates delete`, `templates construct`,
`templates construction`, `templates library`, `templates copy`,
internal/commands/templates.go:307
The new card-table completion formatter is not exercised: TestTemplatesCopyStatusStates invokes only the todolists duplication command, while the card-table test stops at the pending response. A regression in DestinationCardTable handling or its cards breadcrumb would therefore silently fall back to the generic completion message; add a completed card-table duplication case.
The exported constructor's documentation still says it manages only project and to-do list templates, while this command now also owns card-table templates. Update the Go doc comment so generated API documentation matches the expanded command scope.
Short: "Manage project, to-do list, and card table templates",
internal/commands/templates.go:1096
The construct command's long help still tells users to poll templates construction, but this change makes templates projects construction the canonical status command and updates the emitted breadcrumb accordingly. Update that help text as well; otherwise users following construct --help are directed to the old flat path.
Cmd: fmt.Sprintf("basecamp templates projects construction %d %d", templateID, construction.ID),
skills/basecamp/SKILL.md:1035
The skill now tells agents that all flat template spellings have been removed, while the PR description says they continue to resolve. This will cause generated agent instructions to reject commands that the stated compatibility contract requires; update it consistently with the final command behavior.
**Every verb lives under its kind.** The flat pre-grouping spellings
(`templates list`, `templates show`, `templates create`, `templates update`,
`templates delete`, `templates construct`, `templates construction`,
`templates library`, `templates copy`, `templates copy-status`) have been
removed — use `templates projects delete`, not `templates delete`. `copy` and
Files reviewed: 16/16 changed files
Comments generated: 3
Review effort level: Lite (auto)
Note
Copilot is running an experiment and ran this review at Lite.
I was following the pattern of the existing construct/construction verb/noun pairs for the project template commands. If we're willing to allow breaking changes for those too, I think it would be nice to coallesce around a "create"/"creation" command pair at the root of the resource in the CLI, with templates being an opt-in choice via a --from flag.
The templates command flattened two kinds of template into one list of
verbs, so a command's name never said whether it acted on a project
template or a to-do list template. The only way to find out was to run one
and read what came back. Basecamp's own template library keeps the kinds
apart, and the CLI now does too.
Duplicating follows the product, which calls the operation duplicating
wherever it appears. The API resource stays a copy.
Every spelling that shipped before still works. Keeping them visible
rather than hidden is deliberate: the surface snapshot skips hidden
commands, so hiding them would record them as removed while they still
work.
The address this listing read predates the library holding more than one
kind of template. It survives only as a redirect, and the SDK now marks it
deprecated, so a newly grouped command would have been built on a call
already scheduled to disappear.
The template library has held card table templates since the API gained
them, and the CLI could not see them. Someone who had built a board worth
reusing could reach it from Basecamp but not from a script.
A card table duplicate lands on the destination project rather than in one
of its tools, so this command takes no container flag. A project has
exactly one dock.
This does not build against the released SDK. The operations it needs are
not published yet, so the dependency is deliberately left where it was.
Bump it before merging.
… board
The card table half of the template library could be seeded from the CLI and
the to-do list half could not, so someone scripting the library had to open
Basecamp for one kind and not the other. The asymmetry was never intended.
It existed because the SDK wrapped one endpoint before the other.
Building a template meant starting from an empty one and retyping work that
already existed somewhere. Basecamp itself offers this from the list's own
menu, and the route is nested under the recording, so the verb belongs on the
to-do list rather than in the template library.
The save runs asynchronously and hands back an ID to poll, matching how
constructing a project and duplicating a template already behave. Polling
inside the command would leave someone who interrupted it holding a finished
template with no way to ask about it.
Templatify and templatification are made-up words, and the only honest ones
available. Construct pairs with construction and duplicate with duplication,
each naming the async record it polls, and saving work as a template has no
plain English verb and noun to take those slots. The help text carries the
definition.
Saving existing work as a template reached to-do lists but not card tables,
even though the same endpoint serves both. Card tables had no home in the CLI
for actions on the table itself: the cards group manages what is inside a
board, and its card-table flag only picks which board to look in.
Gathering the cards into Triage is offered only here. bc3 accepts that
attribute on any recording but never reads it for a to-do list, so a
per-group command is what keeps the invalid combination unreachable.
The e2e suite needs bats, and nothing in the repo installed it. CI cloned a
pinned tag, but a local checkout was left to find bats on its own, so bin/ci
failed at the e2e step on a machine that had no bats or had a mise shim
shadowing one. Pinning it alongside the other tools makes bin/setup enough to
get a green local run.
Basecamp offers to archive or delete a to-do list or card table template,
and the CLI could do neither. The verbs that looked like they would serve
project templates only, and return 404 for anything else in the library.
The flat spellings go at the same time. A verb sitting directly under
templates cannot say which kind of template it acts on, which is how a
library template ended up at the project-template route to begin with.
Every verb now lives under the kind it acts on, so there is one way to
spell each one.
templates list, show, create, update, delete, construct and construction have
shipped since v0.1.0, and library, copy and copy-status since v0.10.0, so
scripts and older skill copies still call them. Each now runs its grouped
command, stays out of help and completion, and prints one deprecation line to
stderr naming the replacement; stdout and --json are unchanged.
STYLE.md now says to hide and deprecate shipped spellings rather than remove
them. API-COVERAGE.md, the construct help and the skill point at the grouped
paths.
Thanks for this. Grouping the templates commands by kind is a real improvement, and it's working well against a dev Basecamp. I tested every command in the new tree, including templatify with and without --move-cards-to-triage.
I've rebased it onto main and added one thing: the old flat spellings still work, as hidden, deprecated aliases. Before, they returned usage errors.
The reason is how long the old ones have been around. The project-template commands have shipped in every release since v0.1.0, and library, copy and copy-status since v0.10.0. So people's scripts use them, and so do agents running an older copy of the Basecamp skill, which keep calling the old commands until their plugin updates.
The aliases are hidden from --help and completion, so the grouped tree is still the only thing people discover, which keeps the point of this PR. Each one prints a one-line deprecation notice to stderr pointing at the new command, and the output is otherwise unchanged. We can remove them properly in a later release. I've updated the description and the STYLE.md rule to match.
Avoid card-table breadcrumbs without a required --in bucket
internal/commands/templates.go:359
The completed card-table breadcrumb is not runnable without an already configured project: cards list selects its account-wide branch when --in is absent and explicitly rejects --card-table there (internal/commands/cards.go:174,282-286). Include the destination card table's bucket as --in; if the response lacks a bucket, omit the breadcrumb rather than emitting a command that fails.
The reason will be displayed to describe this comment to others. Learn more.
6 issues found across 17 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="e2e/smoke/smoke_account.bats">
<violation number="1" location="e2e/smoke/smoke_account.bats:108">
P2: This skips the smoke test for every failure, including CLI regressions and server errors, rather than only an account where card-table templates are unavailable. Skip only for the known unavailable response and let other failures reach `assert_success`.</violation>
</file>
<file name="internal/commands/templates.go">
<violation number="1" location="internal/commands/templates.go:356">
P2: This follow-up omits the duplicated table's project, so `cards list` can use a different default project or fall back to account-wide mode and reject `--card-table`. Include the destination project ID with `--in`.</violation>
</file>
<file name="skills/basecamp/SKILL.md">
<violation number="1" location="skills/basecamp/SKILL.md:1033">
P2: Document the supported `--name`, `--copy-comments`, and `--copy-assignments` overrides here; otherwise agents following this skill cannot control the saved template's title or whether comments and assignments carry over.</violation>
</file>
<file name="e2e/templates.bats">
<violation number="1" location="e2e/templates.bats:185">
P2: This test passes regardless of whether `templates foobar` succeeds or reports an error. Assert failure and verify the unknown-command error.</violation>
<violation number="2" location="e2e/templates.bats:287">
P2: These new missing-name tests do not verify the usage error code. Assert `.code` is `usage` as well as checking `.error`.
(Based on your team's feedback about structured usage-error assertions.)</violation>
</file>
<file name="STYLE.md">
<violation number="1" location="STYLE.md:55">
P3: `because hidden commands are` stops mid-clause — hidden commands are excluded from what? Finish the thought so the rule reads as one idea: "They are not in `.surface` because hidden commands are omitted from it, so their entries in `.surface-breaking` stay."</violation>
</file>
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
The reason will be displayed to describe this comment to others. Learn more.
P2: This skips the smoke test for every failure, including CLI regressions and server errors, rather than only an account where card-table templates are unavailable. Skip only for the known unavailable response and let other failures reach assert_success.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At e2e/smoke/smoke_account.bats, line 108:
<comment>This skips the smoke test for every failure, including CLI regressions and server errors, rather than only an account where card-table templates are unavailable. Skip only for the known unavailable response and let other failures reach `assert_success`.</comment>
<file context>
@@ -102,3 +102,10 @@ setup_file() {
+
+@test "templates card-tables list returns card table templates" {
+ run_smoke basecamp templates card-tables list --json
+ [[ "$status" -ne 0 ]] && mark_unverifiable "Card table template library not available in this account"
+ assert_success
+ assert_json_value '.ok' 'true'
</file context>
The reason will be displayed to describe this comment to others. Learn more.
P2: This follow-up omits the duplicated table's project, so cards list can use a different default project or fall back to account-wide mode and reject --card-table. Include the destination project ID with --in.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/commands/templates.go, line 356:
<comment>This follow-up omits the duplicated table's project, so `cards list` can use a different default project or fall back to account-wide mode and reject `--card-table`. Include the destination project ID with `--in`.</comment>
<file context>
@@ -37,14 +103,294 @@ Library templates copy a reusable to-do list into an existing project.`,
+ breadcrumbs := []output.Breadcrumb{
+ {
+ Action: "cards",
+ Cmd: fmt.Sprintf("basecamp cards list --card-table %d%s", table.ID, contextArgs),
+ Description: "List cards on the duplicated board",
+ },
</file context>
Suggested change
Cmd: fmt.Sprintf("basecamp cards list --card-table %d%s", table.ID, contextArgs),
The reason will be displayed to describe this comment to others. Learn more.
P2: Document the supported --name, --copy-comments, and --copy-assignments overrides here; otherwise agents following this skill cannot control the saved template's title or whether comments and assignments carry over.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At skills/basecamp/SKILL.md, line 1033:
<comment>Document the supported `--name`, `--copy-comments`, and `--copy-assignments` overrides here; otherwise agents following this skill cannot control the saved template's title or whether comments and assignments carry over.</comment>
<file context>
@@ -984,30 +984,81 @@ basecamp recordings visibility <id> --hidden # Hide from clients
+
+A templatification is the record of a save in progress, named to match
+`construction` and `duplication`. Every attribute of a save defaults server-side, so `templatify <id>` with
+no flags is a complete request and the template takes the source's title.
+`--move-cards-to-triage` gathers the cards into Triage instead of leaving them
+where they sit, and is card tables only.
</file context>
Suggested change
no flags is a complete request and the template takes the source's title.
no flags is a complete request and the template takes the source's title; use --name, --copy-comments, and --copy-assignments to override the corresponding defaults.
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At e2e/templates.bats, line 287:
<comment>These new missing-name tests do not verify the usage error code. Assert `.code` is `usage` as well as checking `.error`.
(Based on your team's feedback about structured usage-error assertions.) </comment>
<file context>
@@ -15,201 +15,345 @@ load test_helper
-@test "templates --help shows help" {
+ run basecamp templates card-tables create
+ assert_failure
+ assert_json_value '.error' '<name> required'
+}
+
</file context>
The reason will be displayed to describe this comment to others. Learn more.
P2: This test passes regardless of whether templates foobar succeeds or reports an error. Assert failure and verify the unknown-command error.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At e2e/templates.bats, line 185:
<comment>This test passes regardless of whether `templates foobar` succeeds or reports an error. Assert failure and verify the unknown-command error.</comment>
<file context>
@@ -15,201 +15,345 @@ load test_helper
create_global_config '{"account_id": 99999}'
- run basecamp templates copy
+ run basecamp templates foobar
+ # Command may show help or require project - just verify it runs
+}
</file context>
The reason will be displayed to describe this comment to others. Learn more.
P3: because hidden commands are stops mid-clause — hidden commands are excluded from what? Finish the thought so the rule reads as one idea: "They are not in .surface because hidden commands are omitted from it, so their entries in .surface-breaking stay."
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At STYLE.md, line 55:
<comment>`because hidden commands are` stops mid-clause — hidden commands are excluded from what? Finish the thought so the rule reads as one idea: "They are not in `.surface` because hidden commands are omitted from it, so their entries in `.surface-breaking` stay."</comment>
<file context>
@@ -40,6 +40,21 @@ the canonical `<group> <action>` form (`cards create`, `todos create`,
+`templates construction`, `templates library`, `templates copy`,
+`templates copy-status`) still run their grouped command, hidden from help and
+completion, and print one deprecation line to stderr naming the replacement.
+They are out of `.surface` because hidden commands are, so their entries in
+`.surface-breaking` stay. Never document or suggest a deprecated spelling.
+
</file context>
Suggested change
They are out of`.surface` because hidden commands are, so their entries in
`.surface-breaking` stay. Never document or suggest a deprecated spelling.
They are not in`.surface` because hidden commands are omitted from it, so their entries in
Hey @thehale, picking this back up. Jeremy's change request ("a more fluent CLI grammar rather than reflecting the underlying resources verbatim") is still open, and I think your reply was most of the way there. Here's a version that builds on it, for you and @jeremy to push back on.
First, so nothing surprises you: I've rebased this branch onto main (twice; the second time on top of the new subtasks commands), added a commit that keeps the shipped flat spellings as hidden, deprecated aliases, and removed some duplicated smoke tests. Every command in the current tree is tested live against a dev Basecamp, so whatever grammar we settle on, the API calls underneath are known to work.
The idea: name commands for what a person wants, not for the API's jobs (construction, duplication, templatification).
Make a thing from a template: the thing's own create, with --template
Manage templates with the standard verbs:templates projects|todolists|card-tables list/show/update/archive/trash/restore/delete.
Slow jobs wait by default. Each of those waits for Basecamp to finish and prints the finished thing. --no-wait returns the job straight away, plus the exact command to check on it (templates <kind> status …). That removes the -tion status nouns for almost everyone, agents included: today they're told to poll.
This differs from your sketch in two ways:
Separate flags.--template only ever means "made from a template", and --from only ever means "template made from this". --from already means a date (timesheet) and a person (unassign), so giving it two more meanings felt like too much.
Waiting instead of creation commands. Waiting by default replaces the separate creation commands.
Compatibility: only the spellings that shipped get hidden, deprecated aliases. The names that only exist in this PR (duplicate, templatify and so on) never shipped, so they can simply change.
Shipped spelling
Runs
templates list/show/create/update/delete
templates projects …
templates construct <id> --name X
projects create X --template <id> --no-wait
templates construction
templates projects status
templates library
templates todolists list
templates copy <id> --in P
todolists create --template <id> --in P --no-wait
templates copy-status
templates todolists status
The old slow commands map onto --no-wait, so scripts that poll keep working exactly as before.
@jeremy, does this get at what you meant? @thehale, keen to hear what you think, and happy to talk any of it through.
I'm happy with your latest approach. I do slightly prefer the look of project create --from <template-id>, but your proposal of project create --template <id> is just as clear and your concern about conceptual overload of --from is compelling.
Waiting is also fine, though it may be worthwhile to have some sort of indicator for long copies. Constructing a new template from a large project/card table/etc. can take a few minutes -- more than long enough that I'd be tempted to press CTRL+C thinking that the process had gotten stuck.
Waiting is also fine, though it may be worthwhile to have some sort of indicator for long copies.
Maybe we ditch waiting and just go with polling by default. Agents don't care. So you trigger, get back a polling url and check that.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The template library holds three kinds of template. Basecamp's own navigation separates them, and the CLI did not:
templates listmeant project templates,templates librarymeant to-do list templates, and nothing in either name said so. You found out by running one and reading what came back.This groups the commands by the kind they act on, adds card table templates, and adds a way to turn work you already have into a template.
The command tree
The flat spellings are hidden and deprecated, not removed.
templates list,show,create,update,delete,constructandconstructionruntemplates projects …;templates libraryrunstemplates todolists list;templates copyandcopy-statusruntemplates todolists duplicateandduplication. They're out of--helpand completion, so the grouped tree is the only one people discover, and each prints a one-line deprecation notice to stderr naming the new command. Output is otherwise unchanged.copyandcopy-statusalso survive as aliases inside the grouped paths.The 252 surface lines the grouped tree replaces are acknowledged in
.surface-breaking(hidden commands aren't in.surface), andSTYLE.mdnow says to hide and deprecate shipped spellings rather than remove them.Taking a template out of the library
Basecamp offers to archive or delete a to-do list or card table template, and the CLI could do neither.
templates showandtemplates deleteroute to project templates and answer 404 for anything in the library.A library template is a recording, so
archive,trashandrestorego through the recordings status endpoint. They are spelled the way every other recording in this CLI is:cards,files,messages,commentsandtodolistsall usetrash/archive/restoreoff one shared helper, sodeletehere would have made the templates tree the only place the same operation on the same kind of object gets a different verb.templates projects deletekeeps its name because it is a different endpoint.restorehas no UI equivalent under that name; it is the CLI spelling of the "unarchive it" affordance on an archived template.Naming
Three asynchronous operations now read the same way, each status command named after the record it polls:
duplicatefollows the product, which calls the operation Duplicate everywhere it appears. The API resource stayscopiesand the SDK operation staysCreateLibraryCopy; that separation is deliberate and matches what the front-end already does, where the UI says Duplicate and the route says copies.templatifyandtemplatificationare coined words, and they are the only honest ones available. The product has no name for this operation at all, so rather than borrowing an unrelated English word that would read ambiguously next totodolists' other verbs, the CLI takes the resource noun. The help text carries the definition at the point you meet it.A card table group
There was no home for actions on a card table itself.
cardsmanages what is inside a board, and its--card-tableflag only picks which board to look in.basecamp card-tablesis that home, and it starts with one verb because a command group is a resource identity rather than a subcommand quota.messageboardshas exactly one action for the same reason.Duplicating names the project, not the container
duplicatesends the destination project and Basecamp resolves the container from the template's kind: a to-do list into the project's To-dos tool, a card table onto its dock.--todosetstill pins the container when a project has more than one to-do set, and still checks that it belongs to the project and is enabled, because a clear message beats a 404.card-tables duplicatehas no container flag, because a project has exactly one dock.Verification
Unit tests cover request shape and output rendering. e2e covers error paths, flag surface and help.
Run against a live server with a seeded database:
templatify <id>with no flags sends{}. The template came back carrying the source's title, "Strategy ideas".card-tables templatifyon the same board twice, with and without--move-cards-to-triage. With it, all 7 cards including one under an on-hold container are inKanban::Triage. Without it,Triage 1, Column 4, NotNowColumn 1, OnHold 1. The two results differ, so the flag reaches the wire rather than being dropped.templatificationon both kinds reports a real template name and id, and the matchingdestination_todolist/destination_card_tableis populated, so neither falls through to the generic completion message.templatificationwith its two ids transposed exits 2 withnot_found, rather than reading a different record.templatifyon a recording that cannot be templatified exits 4 withforbidden, which is a different code and shape from the transposed case, so the two stay distinguishable.templates todolists create,templates todolists listandtemplates card-tables listall succeed, and everything created during the run appears in the listings.archive,trashandrestorewere not exercised against a live account from this branch. Unit tests pin each verb to its exact route (/:account/recordings/:id/status/{archived,trashed,active}.json) and assert a non-numeric id is rejected before any request goes out. The endpoints themselves were verified end to end against a local bc3 for both template kinds: each call returned 204 and the status transition was confirmed in the database.Depends on
basecamp/basecamp-sdk#877
Three commits use SDK operations added in basecamp-sdk#877 and do not compile against the current release. Bump
go.modbefore merging.