thought-cabinet 0.1.10 → 0.1.12
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/README.md +10 -10
- package/dist/index.js +42 -27
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +8 -8
- package/docs/WORKTREES.md +2 -2
- package/package.json +12 -15
- package/src/agent-assets/skills/creating-plan/SKILL.md +60 -45
- package/src/agent-assets/skills/creating-plan/plan-template.md +1 -0
- package/src/agent-assets/skills/implementing-plan/SKILL.md +37 -4
- package/src/agent-assets/skills/{researching-codebase → research-codebase}/SKILL.md +2 -2
- package/src/agent-assets/skills/test-driven-development/SKILL.md +201 -0
- /package/src/agent-assets/skills/{researching-codebase → research-codebase}/research-template.md +0 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Thought Cabinet solves these by providing:
|
|
|
24
24
|
cd your-project
|
|
25
25
|
|
|
26
26
|
# 1. Install
|
|
27
|
-
|
|
27
|
+
pnpm install -g thought-cabinet
|
|
28
28
|
|
|
29
29
|
# 2. Initialize thoughts in your project
|
|
30
30
|
thc init
|
|
@@ -33,7 +33,7 @@ thc init
|
|
|
33
33
|
thc agent init
|
|
34
34
|
|
|
35
35
|
# 4. Use skills in your agent session (e.g. Claude Code)
|
|
36
|
-
> /
|
|
36
|
+
> /research-codebase How does the authentication system work?
|
|
37
37
|
> /creating-plan Add OAuth2 support based on the research
|
|
38
38
|
> /implementing-plan thoughts/shared/plans/add-oauth.md
|
|
39
39
|
> /validating-plan thoughts/shared/plans/add-oauth.md
|
|
@@ -43,14 +43,14 @@ thc agent init
|
|
|
43
43
|
|
|
44
44
|
Skills are installed by `thc agent init` and invoked as slash commands in your agent session:
|
|
45
45
|
|
|
46
|
-
| Skill
|
|
47
|
-
|
|
|
48
|
-
| `/
|
|
49
|
-
| `/creating-plan`
|
|
50
|
-
| `/iterating-plan`
|
|
51
|
-
| `/implementing-plan`
|
|
52
|
-
| `/validating-plan`
|
|
53
|
-
| `/commit`
|
|
46
|
+
| Skill | Description |
|
|
47
|
+
| -------------------- | --------------------------------------------------------------------- |
|
|
48
|
+
| `/research-codebase` | Deep-dive into codebase, save findings to `thoughts/shared/research/` |
|
|
49
|
+
| `/creating-plan` | Create implementation plan with phases and success criteria |
|
|
50
|
+
| `/iterating-plan` | Refine existing plans based on feedback |
|
|
51
|
+
| `/implementing-plan` | Execute plan phase-by-phase with verification |
|
|
52
|
+
| `/validating-plan` | Verify implementation against plan's success criteria |
|
|
53
|
+
| `/commit` | Create git commits with clear, descriptive messages |
|
|
54
54
|
|
|
55
55
|
**Typical workflow**: research the codebase to build understanding, create a plan, iterate until the plan is solid, implement it, then validate the result.
|
|
56
56
|
|
package/dist/index.js
CHANGED
|
@@ -1721,12 +1721,34 @@ function getLastCommit(repoPath) {
|
|
|
1721
1721
|
return "No commits yet";
|
|
1722
1722
|
}
|
|
1723
1723
|
}
|
|
1724
|
-
|
|
1724
|
+
var STALE_FETCH_THRESHOLD_HOURS = 6;
|
|
1725
|
+
function getFetchAgeMs(repoPath) {
|
|
1726
|
+
const fetchHead = path13.join(repoPath, ".git", "FETCH_HEAD");
|
|
1727
|
+
try {
|
|
1728
|
+
const stat = fs10.statSync(fetchHead);
|
|
1729
|
+
return Date.now() - stat.mtimeMs;
|
|
1730
|
+
} catch {
|
|
1731
|
+
return null;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function formatDuration(ms) {
|
|
1735
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1736
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1737
|
+
const minutes = Math.floor(seconds / 60);
|
|
1738
|
+
if (minutes < 60) return `${minutes}m`;
|
|
1739
|
+
const hours = Math.floor(minutes / 60);
|
|
1740
|
+
if (hours < 24) return `${hours}h`;
|
|
1741
|
+
const days = Math.floor(hours / 24);
|
|
1742
|
+
return `${days}d`;
|
|
1743
|
+
}
|
|
1744
|
+
function getRemoteStatus(repoPath, doFetch) {
|
|
1725
1745
|
try {
|
|
1726
1746
|
execSync5("git remote get-url origin", { cwd: repoPath, stdio: "pipe" });
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1747
|
+
if (doFetch) {
|
|
1748
|
+
try {
|
|
1749
|
+
execSync5("git fetch", { cwd: repoPath, stdio: "pipe" });
|
|
1750
|
+
} catch {
|
|
1751
|
+
}
|
|
1730
1752
|
}
|
|
1731
1753
|
const status = execSync5("git status -sb", {
|
|
1732
1754
|
cwd: repoPath,
|
|
@@ -1738,26 +1760,7 @@ function getRemoteStatus(repoPath) {
|
|
|
1738
1760
|
return chalk8.yellow(`${ahead} commits ahead of remote`);
|
|
1739
1761
|
} else if (status.includes("behind")) {
|
|
1740
1762
|
const behind = status.match(/behind (\d+)/)?.[1] || "?";
|
|
1741
|
-
|
|
1742
|
-
execSync5("git pull --rebase", {
|
|
1743
|
-
stdio: "pipe",
|
|
1744
|
-
cwd: repoPath
|
|
1745
|
-
});
|
|
1746
|
-
console.log(chalk8.green("\u2713 Automatically pulled latest changes"));
|
|
1747
|
-
const newStatus = execSync5("git status -sb", {
|
|
1748
|
-
encoding: "utf8",
|
|
1749
|
-
cwd: repoPath,
|
|
1750
|
-
stdio: "pipe"
|
|
1751
|
-
});
|
|
1752
|
-
if (newStatus.includes("behind")) {
|
|
1753
|
-
const newBehind = newStatus.match(/behind (\d+)/)?.[1] || "?";
|
|
1754
|
-
return chalk8.yellow(`${newBehind} commits behind remote (after pull)`);
|
|
1755
|
-
} else {
|
|
1756
|
-
return chalk8.green("Up to date with remote (after pull)");
|
|
1757
|
-
}
|
|
1758
|
-
} catch {
|
|
1759
|
-
return chalk8.yellow(`${behind} commits behind remote`);
|
|
1760
|
-
}
|
|
1763
|
+
return chalk8.yellow(`${behind} commits behind remote`);
|
|
1761
1764
|
} else {
|
|
1762
1765
|
return chalk8.green("Up to date with remote");
|
|
1763
1766
|
}
|
|
@@ -1812,7 +1815,19 @@ async function thoughtsStatusCommand(options) {
|
|
|
1812
1815
|
console.log(chalk8.gray(` (using profile: ${profileName})`));
|
|
1813
1816
|
}
|
|
1814
1817
|
console.log(` ${getGitStatus(expandedRepo)}`);
|
|
1815
|
-
|
|
1818
|
+
const doFetch = options.fetch ?? false;
|
|
1819
|
+
const staleThresholdMs = (parseInt(options.maxAgeSecs ?? "", 10) || STALE_FETCH_THRESHOLD_HOURS * 60 * 60) * 1e3;
|
|
1820
|
+
console.log(` Remote: ${getRemoteStatus(expandedRepo, doFetch)}`);
|
|
1821
|
+
if (!doFetch) {
|
|
1822
|
+
const fetchAgeMs = getFetchAgeMs(expandedRepo);
|
|
1823
|
+
if (fetchAgeMs === null) {
|
|
1824
|
+
console.log(chalk8.gray(" (never fetched, use --fetch to refresh)"));
|
|
1825
|
+
} else if (fetchAgeMs > staleThresholdMs) {
|
|
1826
|
+
console.log(
|
|
1827
|
+
chalk8.gray(` (last fetched ${formatDuration(fetchAgeMs)} ago, use --fetch to refresh)`)
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1816
1831
|
console.log(` Last commit: ${getLastCommit(expandedRepo)}`);
|
|
1817
1832
|
console.log("");
|
|
1818
1833
|
const changes = getUncommittedChanges(expandedRepo);
|
|
@@ -2222,7 +2237,7 @@ function thoughtsCommand(program) {
|
|
|
2222
2237
|
).option("--profile <name>", "Use a specific thoughts profile").action(thoughtsInitCommand);
|
|
2223
2238
|
cmd.command("destroy").description("Remove thoughts setup from current repository").option("--force", "Force removal even if not in configuration").option("--config-file <path>", "Path to config file").action(thoughtsDestoryCommand);
|
|
2224
2239
|
cmd.command("sync").description("Manually sync thoughts to thoughts repository").option("-m, --message <message>", "Commit message for sync").option("--config-file <path>", "Path to config file").action(thoughtsSyncCommand);
|
|
2225
|
-
cmd.command("status").description("Show status of thoughts repository").option("--config-file <path>", "Path to config file").action(thoughtsStatusCommand);
|
|
2240
|
+
cmd.command("status").description("Show status of thoughts repository").option("--config-file <path>", "Path to config file").option("--fetch", "Fetch from remote before showing status").action(thoughtsStatusCommand);
|
|
2226
2241
|
cmd.command("config").description("View or edit thoughts configuration").option("--edit", "Open configuration in editor").option("--json", "Output configuration as JSON").option("--config-file <path>", "Path to config file").action(thoughtsConfigCommand);
|
|
2227
2242
|
cmd.command("prune").description("Remove stale repository mappings (directories that no longer exist)").option("--apply", "Apply changes (default is dry-run)").option("--config-file <path>", "Path to config file").action(thoughtsPruneCommand);
|
|
2228
2243
|
const profile = cmd.command("profile").description("Manage thoughts profiles");
|
|
@@ -3645,7 +3660,7 @@ var OPTIONS = {
|
|
|
3645
3660
|
init: ["--force", "--config-file", "--directory", "--profile"],
|
|
3646
3661
|
destroy: ["--force", "--config-file"],
|
|
3647
3662
|
sync: ["-m", "--message", "--config-file"],
|
|
3648
|
-
status: ["--config-file"],
|
|
3663
|
+
status: ["--config-file", "--fetch"],
|
|
3649
3664
|
config: ["--edit", "--json", "--config-file"],
|
|
3650
3665
|
prune: ["--apply", "--config-file"],
|
|
3651
3666
|
"profile create": ["--repo", "--repos-dir", "--global-dir", "--config-file"],
|