team-toon-tack 2.1.0 â 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +18 -0
- package/README.md +52 -8
- package/README.zh-TW.md +52 -8
- package/commands/ttt-done.md +62 -0
- package/commands/ttt-get-issue.md +71 -0
- package/commands/ttt-status.md +65 -0
- package/commands/ttt-sync.md +51 -0
- package/commands/ttt-work-on.md +48 -0
- package/dist/bin/cli.js +15 -8
- package/dist/scripts/config/teams.js +1 -1
- package/dist/scripts/get-issue.d.ts +1 -0
- package/dist/scripts/get-issue.js +61 -0
- package/dist/scripts/init.js +33 -149
- package/dist/scripts/lib/config-builder.d.ts +1 -1
- package/dist/scripts/lib/sync.d.ts +8 -0
- package/dist/scripts/lib/sync.js +41 -29
- package/dist/scripts/sync.js +1 -1
- package/dist/scripts/work-on.js +1 -1
- package/package.json +5 -3
- package/skills/linear-task-manager/SKILL.md +170 -0
- package/templates/claude-code-commands/done-job.md +0 -45
- package/templates/claude-code-commands/sync-linear.md +0 -32
- package/templates/claude-code-commands/work-on.md +0 -62
- package/templates/config.example.toon +0 -37
- package/templates/local.example.toon +0 -16
package/dist/scripts/init.js
CHANGED
|
@@ -400,147 +400,39 @@ async function updateGitignore(tttDir, interactive) {
|
|
|
400
400
|
// Silently ignore gitignore errors
|
|
401
401
|
}
|
|
402
402
|
}
|
|
403
|
-
async function
|
|
403
|
+
async function showPluginInstallInstructions(interactive) {
|
|
404
404
|
if (!interactive) {
|
|
405
|
-
return
|
|
405
|
+
return false;
|
|
406
406
|
}
|
|
407
|
-
console.log("\nđ¤ Claude Code
|
|
408
|
-
|
|
409
|
-
const { install } = await prompts({
|
|
407
|
+
console.log("\nđ¤ Claude Code Plugin:");
|
|
408
|
+
const { showInstructions } = await prompts({
|
|
410
409
|
type: "confirm",
|
|
411
|
-
name: "
|
|
412
|
-
message: "
|
|
410
|
+
name: "showInstructions",
|
|
411
|
+
message: "Show Claude Code plugin installation instructions? (provides /ttt-* commands)",
|
|
413
412
|
initial: true,
|
|
414
413
|
});
|
|
415
|
-
if (!
|
|
416
|
-
return
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
description: "/linear:work-on, /linear:done-job, /linear:sync-linear",
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
title: "Custom...",
|
|
441
|
-
value: "custom",
|
|
442
|
-
description: "Enter your own prefix",
|
|
443
|
-
},
|
|
444
|
-
],
|
|
445
|
-
initial: 0,
|
|
446
|
-
});
|
|
447
|
-
let prefix = prefixChoice || "";
|
|
448
|
-
if (prefixChoice === "custom") {
|
|
449
|
-
const { customPrefix } = await prompts({
|
|
450
|
-
type: "text",
|
|
451
|
-
name: "customPrefix",
|
|
452
|
-
message: "Enter custom prefix (e.g., 'my:'):",
|
|
453
|
-
initial: "",
|
|
454
|
-
});
|
|
455
|
-
prefix = customPrefix || "";
|
|
456
|
-
}
|
|
457
|
-
// Find templates directory
|
|
458
|
-
// Try multiple locations: installed package, local dev
|
|
459
|
-
const possibleTemplatePaths = [
|
|
460
|
-
path.join(__dirname, "..", "templates", "claude-code-commands"),
|
|
461
|
-
path.join(__dirname, "..", "..", "templates", "claude-code-commands"),
|
|
462
|
-
path.join(process.cwd(), "templates", "claude-code-commands"),
|
|
463
|
-
];
|
|
464
|
-
let templateDir = null;
|
|
465
|
-
for (const p of possibleTemplatePaths) {
|
|
466
|
-
try {
|
|
467
|
-
await fs.access(p);
|
|
468
|
-
templateDir = p;
|
|
469
|
-
break;
|
|
470
|
-
}
|
|
471
|
-
catch {
|
|
472
|
-
// Try next path
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (!templateDir) {
|
|
476
|
-
// Try to get repo URL from package.json
|
|
477
|
-
let repoUrl = "https://github.com/wayne930242/team-toon-tack";
|
|
478
|
-
try {
|
|
479
|
-
const pkgPaths = [
|
|
480
|
-
path.join(__dirname, "..", "package.json"),
|
|
481
|
-
path.join(__dirname, "..", "..", "package.json"),
|
|
482
|
-
];
|
|
483
|
-
for (const pkgPath of pkgPaths) {
|
|
484
|
-
try {
|
|
485
|
-
const pkgContent = await fs.readFile(pkgPath, "utf-8");
|
|
486
|
-
const pkg = JSON.parse(pkgContent);
|
|
487
|
-
if (pkg.repository?.url) {
|
|
488
|
-
// Parse git+https://github.com/user/repo.git format
|
|
489
|
-
repoUrl = pkg.repository.url
|
|
490
|
-
.replace(/^git\+/, "")
|
|
491
|
-
.replace(/\.git$/, "");
|
|
492
|
-
}
|
|
493
|
-
break;
|
|
494
|
-
}
|
|
495
|
-
catch {
|
|
496
|
-
// Try next path
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
// Use default URL
|
|
502
|
-
}
|
|
503
|
-
console.log(" â Could not find command templates. Please copy manually from:");
|
|
504
|
-
console.log(` ${repoUrl}/tree/main/templates/claude-code-commands`);
|
|
505
|
-
return { installed: false, prefix };
|
|
506
|
-
}
|
|
507
|
-
// Create .claude/commands directory
|
|
508
|
-
const commandsDir = path.join(process.cwd(), ".claude", "commands");
|
|
509
|
-
await fs.mkdir(commandsDir, { recursive: true });
|
|
510
|
-
// Copy and rename template files
|
|
511
|
-
const templateFiles = await fs.readdir(templateDir);
|
|
512
|
-
const commandFiles = templateFiles.filter((f) => f.endsWith(".md"));
|
|
513
|
-
for (const file of commandFiles) {
|
|
514
|
-
const baseName = file.replace(".md", "");
|
|
515
|
-
const newFileName = prefix ? `${prefix}${baseName}.md` : file;
|
|
516
|
-
const srcPath = path.join(templateDir, file);
|
|
517
|
-
const destPath = path.join(commandsDir, newFileName);
|
|
518
|
-
// Read template content
|
|
519
|
-
let content = await fs.readFile(srcPath, "utf-8");
|
|
520
|
-
// Update the name in frontmatter if prefix is used
|
|
521
|
-
if (prefix) {
|
|
522
|
-
content = content.replace(/^(---\s*\n[\s\S]*?name:\s*)(\S+)/m, `$1${prefix}${baseName}`);
|
|
523
|
-
}
|
|
524
|
-
// Modify content based on statusSource for work-on and done-job
|
|
525
|
-
if (statusSource === "local") {
|
|
526
|
-
if (baseName === "work-on" || baseName.endsWith("work-on")) {
|
|
527
|
-
// Update description for local mode
|
|
528
|
-
content = content.replace(/Select a task and update status to "In Progress" on both local and Linear\./, 'Select a task and update local status to "In Progress". (Linear will be updated when you run `sync --update`)');
|
|
529
|
-
// Add reminder after Complete section
|
|
530
|
-
content = content.replace(/Use `?\/done-job`? to mark task as completed/, "Use `/done-job` to mark task as completed\n\n### 7. Sync to Linear\n\nWhen ready to update Linear with all your changes:\n\n```bash\nttt sync --update\n```");
|
|
531
|
-
}
|
|
532
|
-
if (baseName === "done-job" || baseName.endsWith("done-job")) {
|
|
533
|
-
// Update description for local mode
|
|
534
|
-
content = content.replace(/Mark a task as done and update Linear with commit details\./, "Mark a task as done locally. (Run `ttt sync --update` to push changes to Linear)");
|
|
535
|
-
// Add reminder at the end
|
|
536
|
-
content = content.replace(/## What It Does\n\n- Linear issue status â "Done"/, "## What It Does\n\n- Local status â `completed`");
|
|
537
|
-
content += `\n## Sync to Linear\n\nAfter completing tasks, push all changes to Linear:\n\n\`\`\`bash\nttt sync --update\n\`\`\`\n`;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
await fs.writeFile(destPath, content, "utf-8");
|
|
541
|
-
console.log(` â .claude/commands/${newFileName}`);
|
|
542
|
-
}
|
|
543
|
-
return { installed: true, prefix };
|
|
414
|
+
if (!showInstructions) {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
console.log("\nâââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ");
|
|
418
|
+
console.log("â Install team-toon-tack plugin in Claude Code: â");
|
|
419
|
+
console.log("âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤");
|
|
420
|
+
console.log("â â");
|
|
421
|
+
console.log("â 1. Add marketplace: â");
|
|
422
|
+
console.log("â /plugin marketplace add wayne930242/team-toon-tack â");
|
|
423
|
+
console.log("â â");
|
|
424
|
+
console.log("â 2. Install plugin: â");
|
|
425
|
+
console.log("â /plugin install team-toon-tack@wayne930242 â");
|
|
426
|
+
console.log("â â");
|
|
427
|
+
console.log("â Available commands after install: â");
|
|
428
|
+
console.log("â /ttt-sync - Sync Linear issues â");
|
|
429
|
+
console.log("â /ttt-work-on - Start working on a task â");
|
|
430
|
+
console.log("â /ttt-done - Complete current task â");
|
|
431
|
+
console.log("â /ttt-status - Show/modify task status â");
|
|
432
|
+
console.log("â /ttt-get-issue - Fetch issue details â");
|
|
433
|
+
console.log("â â");
|
|
434
|
+
console.log("âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ");
|
|
435
|
+
return true;
|
|
544
436
|
}
|
|
545
437
|
async function init() {
|
|
546
438
|
const args = process.argv.slice(2);
|
|
@@ -732,8 +624,8 @@ async function init() {
|
|
|
732
624
|
console.log(` â ${paths.localPath}`);
|
|
733
625
|
// Update .gitignore (always use relative path .ttt)
|
|
734
626
|
await updateGitignore(".ttt", options.interactive ?? true);
|
|
735
|
-
//
|
|
736
|
-
const
|
|
627
|
+
// Show Claude Code plugin installation instructions
|
|
628
|
+
const pluginInstructionsShown = await showPluginInstallInstructions(options.interactive ?? true);
|
|
737
629
|
// Summary
|
|
738
630
|
console.log("\nâ
Initialization complete!\n");
|
|
739
631
|
console.log("Configuration summary:");
|
|
@@ -762,21 +654,13 @@ async function init() {
|
|
|
762
654
|
if (statusTransitions.blocked) {
|
|
763
655
|
console.log(` Blocked: ${statusTransitions.blocked}`);
|
|
764
656
|
}
|
|
765
|
-
if (commandsInstalled) {
|
|
766
|
-
const cmdPrefix = commandPrefix ? `${commandPrefix}` : "";
|
|
767
|
-
console.log(` Claude commands: /${cmdPrefix}work-on, /${cmdPrefix}done-job, /${cmdPrefix}sync-linear`);
|
|
768
|
-
}
|
|
769
657
|
console.log("\nNext steps:");
|
|
770
658
|
console.log(" 1. Set LINEAR_API_KEY in your shell profile:");
|
|
771
659
|
console.log(` export LINEAR_API_KEY="${apiKey}"`);
|
|
772
660
|
console.log(" 2. Run sync: ttt sync");
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
console.log(
|
|
776
|
-
console.log(`\nđĄ Tip: Edit .claude/commands/${cmdPrefix}work-on.md to customize the "Verify" section for your project.`);
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
console.log(" 3. Start working: ttt work-on");
|
|
661
|
+
console.log(" 3. Start working: ttt work-on");
|
|
662
|
+
if (pluginInstructionsShown) {
|
|
663
|
+
console.log("\nđĄ Tip: Install the Claude Code plugin for /ttt-* commands.");
|
|
780
664
|
}
|
|
781
665
|
}
|
|
782
666
|
init().catch(console.error);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompletionMode, Config, LocalConfig, QaPmTeamConfig, StatusTransitions, TeamConfig, UserConfig
|
|
1
|
+
import type { CompletionMode, Config, LabelConfig, LocalConfig, QaPmTeamConfig, StatusTransitions, TeamConfig, UserConfig } from "../utils.js";
|
|
2
2
|
export interface LinearTeam {
|
|
3
3
|
id: string;
|
|
4
4
|
name: string;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { LinearClient } from "@linear/sdk";
|
|
2
2
|
import { type Config, type LocalConfig, type Task } from "../utils.js";
|
|
3
|
+
export interface FetchIssueOptions {
|
|
4
|
+
client?: LinearClient;
|
|
5
|
+
}
|
|
3
6
|
export interface SyncIssueOptions {
|
|
4
7
|
config: Config;
|
|
5
8
|
localConfig: LocalConfig;
|
|
6
9
|
client?: LinearClient;
|
|
7
10
|
preserveLocalStatus?: boolean;
|
|
8
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetch issue details from Linear without saving to local data
|
|
14
|
+
* Returns Task object or null if issue not found
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchIssueDetail(issueId: string, options?: FetchIssueOptions): Promise<Task | null>;
|
|
9
17
|
/**
|
|
10
18
|
* Sync a single issue from Linear and update local cycle data
|
|
11
19
|
* Returns the updated task or null if issue not found
|
package/dist/scripts/lib/sync.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { getLinearClient, loadCycleData, saveCycleData,
|
|
1
|
+
import { getLinearClient, getPrioritySortIndex, loadCycleData, saveCycleData, } from "../utils.js";
|
|
2
2
|
import { getStatusTransitions } from "./linear.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* Returns
|
|
4
|
+
* Fetch issue details from Linear without saving to local data
|
|
5
|
+
* Returns Task object or null if issue not found
|
|
6
6
|
*/
|
|
7
|
-
export async function
|
|
8
|
-
const { config, localConfig: _localConfig, preserveLocalStatus = true, } = options;
|
|
7
|
+
export async function fetchIssueDetail(issueId, options = {}) {
|
|
9
8
|
const client = options.client ?? getLinearClient();
|
|
10
9
|
// Search for the issue
|
|
11
10
|
const searchResult = await client.searchIssues(issueId);
|
|
12
11
|
const matchingIssue = searchResult.nodes.find((i) => i.identifier === issueId);
|
|
13
12
|
if (!matchingIssue) {
|
|
14
|
-
console.error(`Issue ${issueId} not found in Linear.`);
|
|
15
13
|
return null;
|
|
16
14
|
}
|
|
17
15
|
// Fetch full issue data
|
|
@@ -41,34 +39,12 @@ export async function syncSingleIssue(issueId, options) {
|
|
|
41
39
|
user: user?.displayName ?? user?.email,
|
|
42
40
|
};
|
|
43
41
|
}));
|
|
44
|
-
// Determine local status
|
|
45
|
-
let localStatus = "pending";
|
|
46
|
-
const existingData = await loadCycleData();
|
|
47
|
-
if (preserveLocalStatus && existingData) {
|
|
48
|
-
const existingTask = existingData.tasks.find((t) => t.id === issueId);
|
|
49
|
-
if (existingTask) {
|
|
50
|
-
localStatus = existingTask.localStatus;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Map remote status to local status if not preserving
|
|
54
|
-
if (!preserveLocalStatus && state) {
|
|
55
|
-
const transitions = getStatusTransitions(config);
|
|
56
|
-
if (state.name === transitions.done) {
|
|
57
|
-
localStatus = "completed";
|
|
58
|
-
}
|
|
59
|
-
else if (state.name === transitions.in_progress) {
|
|
60
|
-
localStatus = "in-progress";
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
localStatus = "pending";
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
42
|
const task = {
|
|
67
43
|
id: issue.identifier,
|
|
68
44
|
linearId: issue.id,
|
|
69
45
|
title: issue.title,
|
|
70
46
|
status: state ? state.name : "Unknown",
|
|
71
|
-
localStatus:
|
|
47
|
+
localStatus: "pending", // Default, will be overridden by sync if needed
|
|
72
48
|
assignee: assigneeEmail,
|
|
73
49
|
priority: issue.priority,
|
|
74
50
|
labels: labelNames,
|
|
@@ -79,6 +55,42 @@ export async function syncSingleIssue(issueId, options) {
|
|
|
79
55
|
attachments: attachments.length > 0 ? attachments : undefined,
|
|
80
56
|
comments: comments.length > 0 ? comments : undefined,
|
|
81
57
|
};
|
|
58
|
+
return task;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sync a single issue from Linear and update local cycle data
|
|
62
|
+
* Returns the updated task or null if issue not found
|
|
63
|
+
*/
|
|
64
|
+
export async function syncSingleIssue(issueId, options) {
|
|
65
|
+
const { config, localConfig: _localConfig, preserveLocalStatus = true, } = options;
|
|
66
|
+
const client = options.client ?? getLinearClient();
|
|
67
|
+
// Fetch issue details using shared function
|
|
68
|
+
const task = await fetchIssueDetail(issueId, { client });
|
|
69
|
+
if (!task) {
|
|
70
|
+
console.error(`Issue ${issueId} not found in Linear.`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
// Determine local status
|
|
74
|
+
const existingData = await loadCycleData();
|
|
75
|
+
if (preserveLocalStatus && existingData) {
|
|
76
|
+
const existingTask = existingData.tasks.find((t) => t.id === issueId);
|
|
77
|
+
if (existingTask) {
|
|
78
|
+
task.localStatus = existingTask.localStatus;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Map remote status to local status if not preserving
|
|
82
|
+
if (!preserveLocalStatus) {
|
|
83
|
+
const transitions = getStatusTransitions(config);
|
|
84
|
+
if (task.status === transitions.done) {
|
|
85
|
+
task.localStatus = "completed";
|
|
86
|
+
}
|
|
87
|
+
else if (task.status === transitions.in_progress) {
|
|
88
|
+
task.localStatus = "in-progress";
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
task.localStatus = "pending";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
82
94
|
// Update cycle data
|
|
83
95
|
if (existingData) {
|
|
84
96
|
const existingTasks = existingData.tasks.filter((t) => t.id !== issueId);
|
package/dist/scripts/sync.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getLinearClient, getPaths, getPrioritySortIndex, getTeamId, loadConfig, loadCycleData, loadLocalConfig, saveConfig, saveCycleData, } from "./utils.js";
|
|
2
1
|
import { clearIssueImages, downloadLinearImage, ensureOutputDir, extractLinearImageUrls, isLinearImageUrl, } from "./lib/images.js";
|
|
2
|
+
import { getLinearClient, getPaths, getPrioritySortIndex, getTeamId, loadConfig, loadCycleData, loadLocalConfig, saveConfig, saveCycleData, } from "./utils.js";
|
|
3
3
|
async function sync() {
|
|
4
4
|
const args = process.argv.slice(2);
|
|
5
5
|
// Handle help flag
|
package/dist/scripts/work-on.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import prompts from "prompts";
|
|
2
|
-
import {
|
|
2
|
+
import { displayTaskFull, PRIORITY_LABELS } from "./lib/display.js";
|
|
3
3
|
import { getStatusTransitions, updateIssueStatus } from "./lib/linear.js";
|
|
4
4
|
import { getPrioritySortIndex, loadConfig, loadCycleData, loadLocalConfig, saveCycleData, } from "./utils.js";
|
|
5
5
|
async function workOn() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-toon-tack",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Linear task sync & management CLI with TOON format",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
-
"
|
|
16
|
+
".claude-plugin",
|
|
17
|
+
"commands",
|
|
18
|
+
"skills"
|
|
17
19
|
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"build": "tsc",
|
|
20
22
|
"lint": "biome lint .",
|
|
21
|
-
"format": "biome
|
|
23
|
+
"format": "biome check --write .",
|
|
22
24
|
"type": "tsc --noEmit",
|
|
23
25
|
"prepublishOnly": "npm run build",
|
|
24
26
|
"release": "npm config set //registry.npmjs.org/:_authToken=$NPM_PUBLISH_KEY && npm publish"
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear-task-manager
|
|
3
|
+
description: Linear task management expert using ttt CLI. Manages task workflow, syncs issues, tracks status. Use when working with Linear issues, starting tasks, completing work, or checking task status.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Linear Task Manager
|
|
7
|
+
|
|
8
|
+
You are a Linear task management expert using the team-toon-tack (ttt) CLI tool.
|
|
9
|
+
|
|
10
|
+
## Your Role
|
|
11
|
+
|
|
12
|
+
Help developers efficiently manage their Linear workflow:
|
|
13
|
+
- Sync issues from Linear to local cache
|
|
14
|
+
- Start working on tasks
|
|
15
|
+
- Track and update task status
|
|
16
|
+
- Complete tasks with proper documentation
|
|
17
|
+
- Fetch issue details on demand
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
Ensure the project has:
|
|
22
|
+
1. `LINEAR_API_KEY` environment variable set
|
|
23
|
+
2. `.ttt/` directory initialized (run `ttt init` if not)
|
|
24
|
+
3. `ttt` CLI installed (`npm install -g team-toon-tack`)
|
|
25
|
+
|
|
26
|
+
## Core Workflows
|
|
27
|
+
|
|
28
|
+
### 1. Starting a Work Session
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Sync latest issues from Linear
|
|
32
|
+
ttt sync
|
|
33
|
+
|
|
34
|
+
# Pick a task to work on (interactive or auto-select)
|
|
35
|
+
ttt work-on next
|
|
36
|
+
# or
|
|
37
|
+
ttt work-on MP-624
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. During Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Check current task status
|
|
44
|
+
ttt status
|
|
45
|
+
|
|
46
|
+
# Get full issue details
|
|
47
|
+
ttt get-issue MP-624
|
|
48
|
+
|
|
49
|
+
# Mark task as blocked if waiting on dependency
|
|
50
|
+
ttt status MP-624 --set blocked
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Completing Work
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Ensure code is committed first
|
|
57
|
+
git add . && git commit -m "feat: implement feature"
|
|
58
|
+
|
|
59
|
+
# Mark task as done with message
|
|
60
|
+
ttt done -m "Implemented feature with full test coverage"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 4. Syncing Changes
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Pull latest from Linear
|
|
67
|
+
ttt sync
|
|
68
|
+
|
|
69
|
+
# Push local status changes to Linear (if using local mode)
|
|
70
|
+
ttt sync --update
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Status Flow
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
pending â in-progress â completed
|
|
77
|
+
â
|
|
78
|
+
blocked
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Local Status vs Linear Status
|
|
82
|
+
|
|
83
|
+
| Local Status | Linear Status |
|
|
84
|
+
|--------------|---------------|
|
|
85
|
+
| pending | Todo |
|
|
86
|
+
| in-progress | In Progress |
|
|
87
|
+
| completed | Done / Testing |
|
|
88
|
+
| blocked | (configurable) |
|
|
89
|
+
|
|
90
|
+
## Completion Modes
|
|
91
|
+
|
|
92
|
+
The `ttt done` command behaves differently based on configured mode:
|
|
93
|
+
|
|
94
|
+
| Mode | Task Action | Parent Action |
|
|
95
|
+
|------|-------------|---------------|
|
|
96
|
+
| `simple` | â Done | â Done |
|
|
97
|
+
| `strict_review` | â Testing | â QA Testing |
|
|
98
|
+
| `upstream_strict` | â Done | â Testing |
|
|
99
|
+
| `upstream_not_strict` | â Done | â Testing (no fallback) |
|
|
100
|
+
|
|
101
|
+
## File Structure
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
.ttt/
|
|
105
|
+
âââ config.toon # Team configuration
|
|
106
|
+
âââ local.toon # Personal settings
|
|
107
|
+
âââ cycle.toon # Current cycle tasks (auto-generated)
|
|
108
|
+
âââ output/ # Downloaded attachments
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Best Practices
|
|
112
|
+
|
|
113
|
+
### DO
|
|
114
|
+
- Always `ttt sync` before starting work
|
|
115
|
+
- Use `ttt work-on next` for auto-prioritization
|
|
116
|
+
- Include meaningful messages with `ttt done -m "..."`
|
|
117
|
+
- Check `ttt status` to verify state before commits
|
|
118
|
+
|
|
119
|
+
### DON'T
|
|
120
|
+
- Don't manually edit `cycle.toon` - use CLI commands
|
|
121
|
+
- Don't skip sync - local data may be stale
|
|
122
|
+
- Don't forget to commit before `ttt done`
|
|
123
|
+
|
|
124
|
+
## Troubleshooting
|
|
125
|
+
|
|
126
|
+
### "No cycle data found"
|
|
127
|
+
Run `ttt sync` to fetch issues from Linear.
|
|
128
|
+
|
|
129
|
+
### "Issue not found in current cycle"
|
|
130
|
+
The issue may not match filters. Check:
|
|
131
|
+
- Issue is in active cycle
|
|
132
|
+
- Issue status is Todo or In Progress
|
|
133
|
+
- Issue matches configured label filter
|
|
134
|
+
|
|
135
|
+
### "LINEAR_API_KEY not set"
|
|
136
|
+
```bash
|
|
137
|
+
export LINEAR_API_KEY="lin_api_xxxxx"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Examples
|
|
141
|
+
|
|
142
|
+
### Example: Start Daily Work
|
|
143
|
+
```bash
|
|
144
|
+
ttt sync # Get latest issues
|
|
145
|
+
ttt work-on next # Auto-select highest priority
|
|
146
|
+
# Read task details displayed
|
|
147
|
+
git checkout -b feature/mp-624-new-feature
|
|
148
|
+
# Start coding...
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Example: Complete and Move On
|
|
152
|
+
```bash
|
|
153
|
+
git add . && git commit -m "feat: add new feature"
|
|
154
|
+
ttt done -m "Added feature with tests"
|
|
155
|
+
ttt work-on next # Move to next task
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Example: Check Specific Issue
|
|
159
|
+
```bash
|
|
160
|
+
ttt get-issue MP-624 # Fetch from Linear
|
|
161
|
+
ttt get-issue MP-624 --local # Show cached data
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Important Rules
|
|
165
|
+
|
|
166
|
+
- Always verify `LINEAR_API_KEY` is set before operations
|
|
167
|
+
- Run `ttt sync` at the start of each work session
|
|
168
|
+
- Commit code before running `ttt done`
|
|
169
|
+
- Use `--local` flag to avoid API calls when checking cached data
|
|
170
|
+
- Check `.ttt/output/` for downloaded attachments and images
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: done-job
|
|
3
|
-
description: Mark a Linear issue as done with AI summary comment
|
|
4
|
-
arguments:
|
|
5
|
-
- name: issue-id
|
|
6
|
-
description: Linear issue ID (e.g., MP-624). Optional if only one task is in-progress
|
|
7
|
-
required: false
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Complete Task
|
|
11
|
-
|
|
12
|
-
Mark a task as done and update Linear with commit details.
|
|
13
|
-
|
|
14
|
-
## Process
|
|
15
|
-
|
|
16
|
-
### 1. Determine Issue ID
|
|
17
|
-
|
|
18
|
-
Check `.toon/cycle.toon` for tasks with `localStatus: in-progress`.
|
|
19
|
-
|
|
20
|
-
### 2. Write Fix Summary
|
|
21
|
-
|
|
22
|
-
Prepare a concise summary (1-3 sentences) covering:
|
|
23
|
-
- Root cause
|
|
24
|
-
- How it was resolved
|
|
25
|
-
- Key code changes
|
|
26
|
-
|
|
27
|
-
### 3. Run Command
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
ttt done -d .ttt $ARGUMENTS -m "äŋŽåžŠčĒĒæ"
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## What It Does
|
|
34
|
-
|
|
35
|
-
- Linear issue status â "Done"
|
|
36
|
-
- Adds comment with commit hash, message, and diff summary
|
|
37
|
-
- Parent issue (if exists) â "Testing"
|
|
38
|
-
- Local status â `completed` in `.toon/cycle.toon`
|
|
39
|
-
|
|
40
|
-
## Example Usage
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
/done-job MP-624
|
|
44
|
-
/done-job
|
|
45
|
-
```
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sync-linear
|
|
3
|
-
description: Sync Linear Frontend issues to local TOON file
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sync Linear Issues
|
|
7
|
-
|
|
8
|
-
Fetch current cycle's Frontend issues from Linear to `.toon/cycle.toon`.
|
|
9
|
-
|
|
10
|
-
## Process
|
|
11
|
-
|
|
12
|
-
### 1. Run Sync
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
ttt sync -d .ttt
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### 2. Review Output
|
|
19
|
-
|
|
20
|
-
Script displays a summary of tasks in the current cycle.
|
|
21
|
-
|
|
22
|
-
## When to Use
|
|
23
|
-
|
|
24
|
-
- Before starting a new work session
|
|
25
|
-
- When task list is missing or outdated
|
|
26
|
-
- After issues are updated in Linear
|
|
27
|
-
|
|
28
|
-
## Example Usage
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
/sync-linear
|
|
32
|
-
```
|