Skip to content

Commit b42ea8c

Browse files
Add blog post and clean up blog
1 parent c632e12 commit b42ea8c

13 files changed

Lines changed: 594 additions & 16 deletions

CHANGELOG.md

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,74 @@
11
# Changelog
22

3+
## 0.9.0
4+
5+
### Changed
6+
7+
- Repository transferred to the [Nimblesite](https://github.com/Nimblesite/CommandTree) organisation; all links updated
8+
9+
### Fixed
10+
11+
- Release process fixes
12+
- SEO improvements across the website
13+
14+
## 0.8.0
15+
16+
### Changed
17+
18+
- Maintenance release
19+
20+
## 0.7.0
21+
22+
### Added
23+
24+
- **Mise task discovery** — discovers tasks from `mise.toml`, `.mise.toml`, `mise.yaml`, and `.mise.yaml`, including task descriptions
25+
- TOML parser now recognises `[tasks.name]` sections without requiring a bare `[tasks]` preamble
26+
- Pure `parsers/miseParser.ts` extracted for testability
27+
- Lock recovery spec and `[DISC-PARSE-STRATEGY]` parsing strategy spec
28+
- Cross-platform `Makefile` with OS detection, help target, and standardised public/private targets
29+
30+
### Changed
31+
32+
- Bumped supported command count to 22 (adds Mise alongside C# Script and F# Script)
33+
- DB layer simplified — `initSchema`, `registerCommand`, `ensureCommandExists`, `closeDatabase`, tag mutators, `upsertSummary`, etc. now return directly and throw only on unrecoverable errors. New `getDbOrThrow()` replaces repeated `getDb()` + `Result` unwrapping
34+
- `CommandTreeProvider`, `QuickTasksProvider`, `TagConfig`, `extension.ts`, `TaskRunner`, and `summaryPipeline` simplified by removing `Result` unwrapping boilerplate
35+
- `CLAUDE.md` / `Agents.md` consolidated with logging standards, spec ID rules, and updated command table
36+
- `.prettierrc` renamed to `.prettierrc.json` with explicit settings
37+
- `.gitignore` expanded with universal patterns and secret exclusions
38+
- Docs reorganised into `docs/specs/` and `docs/plans/`
39+
- Coverage thresholds updated to reflect current coverage
40+
- README updated for 22 tool types
41+
42+
### Removed
43+
44+
- `.github/workflows/deploy.yml` (superseded by `release.yml` post-release job)
45+
- Stale `CoveragePlan.md`
46+
47+
## 0.6.0
48+
49+
### Added
50+
51+
- **C# Script (.csx) discovery** via new `discovery/csharp-script.ts`
52+
- **F# Script (.fsx) discovery** via new `discovery/fsharp-script.ts`
53+
- Reusable helpers extracted: `powershellParser.ts`, `nodeFactory.ts`, `tagSync.ts`, `watchers.ts`
54+
- New unit test suites: `discovery.unit.test.ts`, `modelSelection.unit.test.ts`, `taskRunner.unit.test.ts`, `treehierarchy.unit.test.ts`
55+
- CI format check, spell check, and 90% coverage threshold
56+
- Claude skills: `ci-prep`, `fix-bug`
57+
- Rust LSP plan and spec docs
58+
59+
### Changed
60+
61+
- Refactored `CommandTreeProvider`, `QuickTasksProvider`, and `TagConfig` for reduced complexity and a more functional style
62+
- Reorganised semantic/db layer — moved `db.ts` from `semantic/` to `db/` and added `lifecycle.ts`
63+
- Replaced fake/indirect tests with proper E2E coverage
64+
- Tightened ESLint, Prettier, and `tsconfig` rules
65+
- Split `SPEC.md` into focused docs
66+
67+
### Removed
68+
69+
- Unused embedding modules: `embedder.ts`, `embeddingPipeline.ts`, `similarity.ts`, `store.ts`, plus related types and index
70+
- Copilot-dependent tests excluded from CI
71+
372
## 0.5.0
473

574
### Added
@@ -39,7 +108,7 @@
39108

40109
### Added
41110

42-
- See [Release 0.2.0](https://github.com/MelbourneDeveloper/CommandTree/releases/tag/v0.2.0)
111+
- See [Release 0.2.0](https://github.com/Nimblesite/CommandTree/releases/tag/v0.2.0)
43112

44113
## 0.1.0 - Initial Release
45114

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
"publisher": "nimblesite",
99
"repository": {
1010
"type": "git",
11-
"url": "https://github.com/MelbourneDeveloper/CommandTree.git"
11+
"url": "https://github.com/Nimblesite/CommandTree.git"
1212
},
1313
"homepage": "https://commandtree.dev/",
1414
"bugs": {
15-
"url": "https://github.com/MelbourneDeveloper/CommandTree/issues"
15+
"url": "https://github.com/Nimblesite/CommandTree/issues"
1616
},
1717
"engines": {
1818
"vscode": "^1.110.0"

website/eleventy.config.js

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,175 @@ export default function(eleventyConfig) {
2020
eleventyConfig.addPassthroughCopy({ "src/favicon.ico": "favicon.ico" });
2121
eleventyConfig.addPassthroughCopy({ "src/site.webmanifest": "site.webmanifest" });
2222

23+
eleventyConfig.addFilter("sortByDateDesc", (items) => {
24+
if (!items) { return []; }
25+
return [...items].sort((a, b) => new Date(b.date) - new Date(a.date));
26+
});
27+
28+
const cardSnippet = `<article class="blog-post">
29+
<a href="{{ cardHref }}" class="post-title">{{ cardTitle }}</a>
30+
<div class="post-meta">{{ cardMeta | safe }}</div>
31+
{% if cardExcerpt %}<p class="post-excerpt">{{ cardExcerpt }}</p>{% endif %}
32+
{% if cardTags and cardTags | length > 0 %}
33+
<div class="post-tags">
34+
{% for t in cardTags %}{% if t != 'post' and t != 'posts' %}<a href="/blog/tags/{{ t | slugify }}/" class="tag">{{ t }}</a>{% endif %}{% endfor %}
35+
</div>
36+
{% endif %}
37+
</article>`;
38+
39+
const blogIndexOverride = `---
40+
layout: layouts/base.njk
41+
title: Blog
42+
permalink: /blog/
43+
---
44+
<div class="blog-container">
45+
<header class="blog-header">
46+
<h1>Blog</h1>
47+
<p class="blog-subtitle">Latest posts and updates</p>
48+
</header>
49+
<nav class="blog-nav">
50+
<a href="/blog/tags/" class="blog-nav-link">Tags</a>
51+
<a href="/blog/categories/" class="blog-nav-link">Categories</a>
52+
</nav>
53+
<div class="post-list">
54+
{% for post in collections.posts | sortByDateDesc %}
55+
{% set cardHref = post.url %}
56+
{% set cardTitle = post.data.title %}
57+
{% set cardMeta %}<time datetime="{{ post.date | isoDate }}">{{ post.date | dateFormat }}</time>{% if post.data.author %} &middot; {{ post.data.author }}{% endif %}{% endset %}
58+
{% set cardExcerpt = post.data.excerpt or post.data.description %}
59+
{% set cardTags = post.data.tags %}
60+
${cardSnippet}
61+
{% endfor %}
62+
</div>
63+
</div>`;
64+
65+
const tagsIndexOverride = `---
66+
layout: layouts/base.njk
67+
title: Tags
68+
permalink: /blog/tags/
69+
---
70+
<div class="blog-container">
71+
<header class="blog-header">
72+
<h1>Tags</h1>
73+
<p class="blog-subtitle">Browse blog posts by tag</p>
74+
</header>
75+
<nav class="blog-nav">
76+
<a href="/blog/" class="blog-nav-link">All posts</a>
77+
<a href="/blog/categories/" class="blog-nav-link">Categories</a>
78+
</nav>
79+
<ul class="taxonomy-grid">
80+
{% for tag in collections.tagList %}
81+
{% set count = collections.postsByTag[tag] | length %}
82+
<li><a href="/blog/tags/{{ tag | slugify }}/" class="taxonomy-card">
83+
<span class="taxonomy-name">{{ tag | capitalize }}</span>
84+
<span class="taxonomy-count">{{ count }} post{% if count != 1 %}s{% endif %}</span>
85+
</a></li>
86+
{% endfor %}
87+
</ul>
88+
</div>`;
89+
90+
const categoriesIndexOverride = `---
91+
layout: layouts/base.njk
92+
title: Categories
93+
permalink: /blog/categories/
94+
---
95+
<div class="blog-container">
96+
<header class="blog-header">
97+
<h1>Categories</h1>
98+
<p class="blog-subtitle">Browse blog posts by category</p>
99+
</header>
100+
<nav class="blog-nav">
101+
<a href="/blog/" class="blog-nav-link">All posts</a>
102+
<a href="/blog/tags/" class="blog-nav-link">Tags</a>
103+
</nav>
104+
<ul class="taxonomy-grid">
105+
{% for category in collections.categoryList %}
106+
{% set count = collections.postsByCategory[category] | length %}
107+
<li><a href="/blog/categories/{{ category | slugify }}/" class="taxonomy-card">
108+
<span class="taxonomy-name">{{ category | capitalize }}</span>
109+
<span class="taxonomy-count">{{ count }} post{% if count != 1 %}s{% endif %}</span>
110+
</a></li>
111+
{% endfor %}
112+
</ul>
113+
{% if (collections.categoryList | length) == 0 %}
114+
<p style="text-align:center;color:var(--color-muted);">No categories yet.</p>
115+
{% endif %}
116+
</div>`;
117+
118+
const tagsPagesOverride = `---
119+
pagination:
120+
data: collections.tagList
121+
size: 1
122+
alias: tag
123+
permalink: /blog/tags/{{ tag | slugify }}/
124+
layout: layouts/base.njk
125+
eleventyComputed:
126+
title: "Posts tagged '{{ tag | capitalize }}'"
127+
description: "All blog posts tagged with {{ tag | capitalize }}."
128+
---
129+
<div class="blog-container">
130+
<header class="blog-header">
131+
<h1>Posts tagged "{{ tag | capitalize }}"</h1>
132+
<p><a href="/blog/tags/">&larr; All tags</a></p>
133+
</header>
134+
<div class="post-list">
135+
{% for post in collections.postsByTag[tag] | sortByDateDesc %}
136+
{% set cardHref = post.url %}
137+
{% set cardTitle = post.data.title %}
138+
{% set cardMeta %}<time datetime="{{ post.date | isoDate }}">{{ post.date | dateFormat }}</time>{% if post.data.author %} &middot; {{ post.data.author }}{% endif %}{% endset %}
139+
{% set cardExcerpt = post.data.excerpt or post.data.description %}
140+
{% set cardTags = post.data.tags %}
141+
${cardSnippet}
142+
{% endfor %}
143+
</div>
144+
</div>`;
145+
146+
const categoriesPagesOverride = `---
147+
pagination:
148+
data: collections.categoryList
149+
size: 1
150+
alias: category
151+
permalink: /blog/categories/{{ category | slugify }}/
152+
layout: layouts/base.njk
153+
eleventyComputed:
154+
title: "{{ category | capitalize }}"
155+
description: "All blog posts in the {{ category }} category."
156+
---
157+
<div class="blog-container">
158+
<header class="blog-header">
159+
<h1>{{ category | capitalize }}</h1>
160+
<p><a href="/blog/categories/">&larr; All categories</a></p>
161+
</header>
162+
<div class="post-list">
163+
{% for post in collections.postsByCategory[category] | sortByDateDesc %}
164+
{% set cardHref = post.url %}
165+
{% set cardTitle = post.data.title %}
166+
{% set cardMeta %}<time datetime="{{ post.date | isoDate }}">{{ post.date | dateFormat }}</time>{% if post.data.author %} &middot; {{ post.data.author }}{% endif %}{% endset %}
167+
{% set cardExcerpt = post.data.excerpt or post.data.description %}
168+
{% set cardTags = post.data.tags %}
169+
${cardSnippet}
170+
{% endfor %}
171+
</div>
172+
</div>`;
173+
174+
const blogOverrides = {
175+
"blog/index.njk": blogIndexOverride,
176+
"blog/tags.njk": tagsIndexOverride,
177+
"blog/categories.njk": categoriesIndexOverride,
178+
"blog/tags-pages.njk": tagsPagesOverride,
179+
"blog/categories-pages.njk": categoriesPagesOverride,
180+
};
181+
// Register as an inline plugin so it runs AFTER the techdoc plugin
182+
// (plugins are processed in addPlugin order, after the user config callback).
183+
// This lets us delete the techdoc-registered virtual templates and replace
184+
// them with our customised versions.
185+
eleventyConfig.addPlugin(function blogOverridesPlugin(ec) {
186+
for (const [path, content] of Object.entries(blogOverrides)) {
187+
delete ec.virtualTemplates[path];
188+
ec.addTemplate(path, content);
189+
}
190+
});
191+
23192
const faviconLinks = [
24193
' <link rel="icon" href="/favicon.ico" sizes="48x48">',
25194
' <link rel="icon" href="/assets/images/favicon.svg" type="image/svg+xml">',
@@ -72,7 +241,21 @@ export default function(eleventyConfig) {
72241
"/blog/ai-summaries-hover/": '/assets/images/ai-summary-banner.png',
73242
};
74243

244+
const blogHeroLogos = {
245+
"/blog/mise-tasks-vscode/": {
246+
src: '/assets/images/mise-logo-light.svg',
247+
alt: 'mise-en-place logo',
248+
},
249+
};
250+
75251
const makeBanner = (href) => {
252+
const logo = blogHeroLogos[href];
253+
if (logo) {
254+
return '<div class="blog-hero-banner">\n'
255+
+ ' <div class="blog-hero-glow"></div>\n'
256+
+ ` <img src="${logo.src}" alt="${logo.alt}" class="blog-hero-logo blog-hero-logo--wide">\n`
257+
+ '</div>';
258+
}
76259
const img = blogHeroImages[href];
77260
if (!img) { return blogHeroDefault; }
78261
return '<div class="blog-hero-banner">\n'
@@ -95,14 +278,32 @@ export default function(eleventyConfig) {
95278
}).join("");
96279
};
97280

281+
// Returns true for pages that list ACTUAL BLOG POSTS (which deserve hero
282+
// banners on each card). The /blog/tags/ and /blog/categories/ index pages
283+
// list tags/categories — not posts — so they must NOT get banners.
284+
const isPostListingUrl = (url) => {
285+
if (!url) { return false; }
286+
if (url === "/blog/") { return true; }
287+
const prefixes = ["/blog/tags/", "/blog/categories/"];
288+
for (const prefix of prefixes) {
289+
if (!url.startsWith(prefix)) { continue; }
290+
if (!url.endsWith("/")) { continue; }
291+
const slug = url.slice(prefix.length, -1);
292+
if (slug.length === 0) { continue; }
293+
if (slug.includes("/")) { continue; }
294+
return true;
295+
}
296+
return false;
297+
};
298+
98299
eleventyConfig.addTransform("blogHero", function(content) {
99300
if (!this.page.outputPath?.endsWith(".html")) {
100301
return content;
101302
}
102303
if (!this.page.url?.startsWith("/blog/")) {
103304
return content;
104305
}
105-
if (this.page.url === "/blog/") {
306+
if (isPostListingUrl(this.page.url)) {
106307
return addBannersToCards(content);
107308
}
108309
if (content.includes('blog-hero-banner')) {
@@ -120,7 +321,7 @@ export default function(eleventyConfig) {
120321
}
121322
const apiLine = "- API Reference: https://commandtree.dev/api/";
122323
const extras = [
123-
"- GitHub: https://github.com/MelbourneDeveloper/CommandTree",
324+
"- GitHub: https://github.com/Nimblesite/CommandTree",
124325
"- VS Code Marketplace: https://marketplace.visualstudio.com/items?itemName=nimblesite.commandtree",
125326
].join("\n");
126327
return content.replace(apiLine, extras);

website/src/_data/i18n.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"blog": {
44
"back": "Back to Blog",
55
"title": "Blog",
6-
"subtitle": "Latest posts and updates"
6+
"subtitle": "Latest posts and updates",
7+
"tags": "Tags",
8+
"categories": "Categories"
79
},
810
"nav": {
911
"docs": "Documentation",

website/src/_data/navigation.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
{
1212
"text": "GitHub",
13-
"url": "https://github.com/MelbourneDeveloper/CommandTree",
13+
"url": "https://github.com/Nimblesite/CommandTree",
1414
"external": true
1515
}
1616
],
@@ -33,7 +33,7 @@
3333
"items": [
3434
{
3535
"text": "GitHub",
36-
"url": "https://github.com/MelbourneDeveloper/CommandTree"
36+
"url": "https://github.com/Nimblesite/CommandTree"
3737
},
3838
{
3939
"text": "VS Code Marketplace",

website/src/_data/site.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"name": "Nimblesite Pty Ltd",
1515
"logo": "/assets/images/logo.png",
1616
"sameAs": [
17-
"https://github.com/MelbourneDeveloper/CommandTree",
17+
"https://github.com/Nimblesite/CommandTree",
1818
"https://marketplace.visualstudio.com/items?itemName=nimblesite.commandtree"
1919
]
2020
}

0 commit comments

Comments
 (0)