@c0x12c/ai-toolkit 1.15.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 +16 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +439 -0
- package/VERSION +1 -0
- package/agents/design-critic.md +127 -0
- package/agents/idea-killer.md +72 -0
- package/agents/infrastructure-expert.md +49 -0
- package/agents/micronaut-backend-expert.md +45 -0
- package/agents/phase-reviewer.md +150 -0
- package/agents/research-planner.md +70 -0
- package/agents/solution-architect-cto.md +49 -0
- package/agents/sre-architect.md +49 -0
- package/agents/team-coordinator.md +111 -0
- package/bin/cli.js +780 -0
- package/claude-md/00-header.md +39 -0
- package/claude-md/01-core.md +105 -0
- package/claude-md/05-database.md +20 -0
- package/claude-md/11-backend-micronaut.md +19 -0
- package/claude-md/20-frontend-react.md +44 -0
- package/claude-md/25-ux-design.md +56 -0
- package/claude-md/30-infrastructure.md +24 -0
- package/claude-md/30-project-mgmt.md +119 -0
- package/claude-md/40-product.md +39 -0
- package/claude-md/50-ops.md +34 -0
- package/claude-md/60-research.md +27 -0
- package/claude-md/90-footer.md +21 -0
- package/commands/spartan/brainstorm.md +134 -0
- package/commands/spartan/brownfield.md +157 -0
- package/commands/spartan/build.md +435 -0
- package/commands/spartan/careful.md +94 -0
- package/commands/spartan/commit-message.md +112 -0
- package/commands/spartan/content.md +17 -0
- package/commands/spartan/context-save.md +161 -0
- package/commands/spartan/contribute.md +140 -0
- package/commands/spartan/daily.md +42 -0
- package/commands/spartan/debug.md +308 -0
- package/commands/spartan/deep-dive.md +55 -0
- package/commands/spartan/deploy.md +207 -0
- package/commands/spartan/e2e.md +264 -0
- package/commands/spartan/env-setup.md +166 -0
- package/commands/spartan/epic.md +199 -0
- package/commands/spartan/fe-review.md +181 -0
- package/commands/spartan/figma-to-code.md +260 -0
- package/commands/spartan/forensics.md +46 -0
- package/commands/spartan/freeze.md +84 -0
- package/commands/spartan/fundraise.md +53 -0
- package/commands/spartan/gate-review.md +229 -0
- package/commands/spartan/gsd-upgrade.md +376 -0
- package/commands/spartan/guard.md +42 -0
- package/commands/spartan/init-project.md +178 -0
- package/commands/spartan/init-rules.md +298 -0
- package/commands/spartan/interview.md +154 -0
- package/commands/spartan/kickoff.md +73 -0
- package/commands/spartan/kotlin-service.md +109 -0
- package/commands/spartan/lean-canvas.md +222 -0
- package/commands/spartan/lint-rules.md +122 -0
- package/commands/spartan/map-codebase.md +124 -0
- package/commands/spartan/migration.md +82 -0
- package/commands/spartan/next-app.md +317 -0
- package/commands/spartan/next-feature.md +212 -0
- package/commands/spartan/onboard.md +326 -0
- package/commands/spartan/outreach.md +16 -0
- package/commands/spartan/phase.md +142 -0
- package/commands/spartan/pitch.md +18 -0
- package/commands/spartan/plan.md +210 -0
- package/commands/spartan/pr-ready.md +202 -0
- package/commands/spartan/project.md +106 -0
- package/commands/spartan/qa.md +222 -0
- package/commands/spartan/research.md +254 -0
- package/commands/spartan/review.md +132 -0
- package/commands/spartan/scan-rules.md +173 -0
- package/commands/spartan/sessions.md +143 -0
- package/commands/spartan/spec.md +131 -0
- package/commands/spartan/startup.md +257 -0
- package/commands/spartan/team.md +570 -0
- package/commands/spartan/teardown.md +161 -0
- package/commands/spartan/testcontainer.md +97 -0
- package/commands/spartan/tf-cost.md +123 -0
- package/commands/spartan/tf-deploy.md +116 -0
- package/commands/spartan/tf-drift.md +100 -0
- package/commands/spartan/tf-import.md +107 -0
- package/commands/spartan/tf-module.md +121 -0
- package/commands/spartan/tf-plan.md +100 -0
- package/commands/spartan/tf-review.md +106 -0
- package/commands/spartan/tf-scaffold.md +109 -0
- package/commands/spartan/tf-security.md +147 -0
- package/commands/spartan/think.md +221 -0
- package/commands/spartan/unfreeze.md +13 -0
- package/commands/spartan/update.md +134 -0
- package/commands/spartan/ux.md +1233 -0
- package/commands/spartan/validate.md +193 -0
- package/commands/spartan/web-to-prd.md +706 -0
- package/commands/spartan/workstreams.md +109 -0
- package/commands/spartan/write.md +16 -0
- package/commands/spartan.md +386 -0
- package/frameworks/00-framework-comparison-guide.md +317 -0
- package/frameworks/01-lean-canvas.md +196 -0
- package/frameworks/02-design-sprint.md +304 -0
- package/frameworks/03-foundation-sprint.md +337 -0
- package/frameworks/04-business-model-canvas.md +391 -0
- package/frameworks/05-customer-development.md +426 -0
- package/frameworks/06-jobs-to-be-done.md +358 -0
- package/frameworks/07-mom-test.md +392 -0
- package/frameworks/08-value-proposition-canvas.md +488 -0
- package/frameworks/09-javelin-board.md +428 -0
- package/frameworks/10-build-measure-learn.md +467 -0
- package/frameworks/11-mvp-approaches.md +533 -0
- package/frameworks/think-before-build.md +593 -0
- package/lib/assembler.js +197 -0
- package/lib/assembler.test.js +159 -0
- package/lib/detector.js +166 -0
- package/lib/detector.test.js +221 -0
- package/lib/packs.js +16 -0
- package/lib/resolver.js +272 -0
- package/lib/resolver.test.js +298 -0
- package/lib/worktree.sh +104 -0
- package/package.json +50 -0
- package/packs/backend-micronaut.yaml +35 -0
- package/packs/backend-nodejs.yaml +15 -0
- package/packs/backend-python.yaml +15 -0
- package/packs/core.yaml +37 -0
- package/packs/database.yaml +21 -0
- package/packs/frontend-react.yaml +24 -0
- package/packs/infrastructure.yaml +40 -0
- package/packs/ops.yaml +16 -0
- package/packs/packs.compiled.json +371 -0
- package/packs/product.yaml +22 -0
- package/packs/project-mgmt.yaml +24 -0
- package/packs/research.yaml +39 -0
- package/packs/shared-backend.yaml +14 -0
- package/packs/ux-design.yaml +21 -0
- package/rules/backend-micronaut/API_DESIGN.md +313 -0
- package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
- package/rules/backend-micronaut/CONTROLLERS.md +388 -0
- package/rules/backend-micronaut/KOTLIN.md +414 -0
- package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
- package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
- package/rules/core/NAMING_CONVENTIONS.md +208 -0
- package/rules/core/SKILL_AUTHORING.md +174 -0
- package/rules/core/TIMEZONE.md +316 -0
- package/rules/database/ORM_AND_REPO.md +289 -0
- package/rules/database/SCHEMA.md +146 -0
- package/rules/database/TRANSACTIONS.md +311 -0
- package/rules/frontend-react/FRONTEND.md +344 -0
- package/rules/infrastructure/MODULES.md +260 -0
- package/rules/infrastructure/NAMING.md +196 -0
- package/rules/infrastructure/PROVIDERS.md +309 -0
- package/rules/infrastructure/SECURITY.md +310 -0
- package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
- package/rules/infrastructure/STRUCTURE.md +234 -0
- package/rules/infrastructure/VARIABLES.md +285 -0
- package/rules/shared-backend/ARCHITECTURE.md +46 -0
- package/rules/ux-design/DESIGN_PROCESS.md +176 -0
- package/skills/api-endpoint-creator/SKILL.md +455 -0
- package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
- package/skills/api-endpoint-creator/examples.md +522 -0
- package/skills/api-endpoint-creator/testing-patterns.md +302 -0
- package/skills/article-writing/SKILL.md +109 -0
- package/skills/article-writing/examples.md +59 -0
- package/skills/backend-api-design/SKILL.md +84 -0
- package/skills/backend-api-design/code-patterns.md +138 -0
- package/skills/brainstorm/SKILL.md +95 -0
- package/skills/browser-qa/SKILL.md +87 -0
- package/skills/browser-qa/playwright-snippets.md +110 -0
- package/skills/ci-cd-patterns/SKILL.md +108 -0
- package/skills/ci-cd-patterns/workflows.md +149 -0
- package/skills/competitive-teardown/SKILL.md +93 -0
- package/skills/competitive-teardown/example-analysis.md +50 -0
- package/skills/content-engine/SKILL.md +131 -0
- package/skills/content-engine/examples.md +72 -0
- package/skills/database-patterns/SKILL.md +72 -0
- package/skills/database-patterns/code-templates.md +114 -0
- package/skills/database-table-creator/SKILL.md +141 -0
- package/skills/database-table-creator/examples.md +552 -0
- package/skills/database-table-creator/kotlin-templates.md +400 -0
- package/skills/database-table-creator/migration-template.sql +68 -0
- package/skills/database-table-creator/validation-checklist.md +337 -0
- package/skills/deep-research/SKILL.md +80 -0
- package/skills/design-intelligence/SKILL.md +268 -0
- package/skills/design-workflow/SKILL.md +127 -0
- package/skills/design-workflow/checklists.md +45 -0
- package/skills/idea-validation/SKILL.md +129 -0
- package/skills/idea-validation/example-report.md +50 -0
- package/skills/investor-materials/SKILL.md +122 -0
- package/skills/investor-materials/example-outline.md +70 -0
- package/skills/investor-outreach/SKILL.md +112 -0
- package/skills/investor-outreach/examples.md +76 -0
- package/skills/kotlin-best-practices/SKILL.md +58 -0
- package/skills/kotlin-best-practices/code-patterns.md +132 -0
- package/skills/market-research/SKILL.md +99 -0
- package/skills/security-checklist/SKILL.md +65 -0
- package/skills/security-checklist/audit-reference.md +95 -0
- package/skills/service-debugging/SKILL.md +116 -0
- package/skills/service-debugging/common-issues.md +65 -0
- package/skills/startup-pipeline/SKILL.md +152 -0
- package/skills/terraform-best-practices/SKILL.md +244 -0
- package/skills/terraform-module-creator/SKILL.md +284 -0
- package/skills/terraform-review/SKILL.md +222 -0
- package/skills/terraform-security-audit/SKILL.md +280 -0
- package/skills/terraform-service-scaffold/SKILL.md +574 -0
- package/skills/testing-strategies/SKILL.md +116 -0
- package/skills/testing-strategies/examples.md +103 -0
- package/skills/testing-strategies/integration-test-setup.md +71 -0
- package/skills/ui-ux-pro-max/SKILL.md +238 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/python-setup.md +146 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/web-to-prd/SKILL.md +478 -0
- package/templates/build-config.yaml +44 -0
- package/templates/commands-config.yaml +55 -0
- package/templates/competitor-analysis.md +60 -0
- package/templates/content/AGENT_TEMPLATE.md +47 -0
- package/templates/content/COMMAND_TEMPLATE.md +27 -0
- package/templates/content/RULE_TEMPLATE.md +40 -0
- package/templates/content/SKILL_TEMPLATE.md +41 -0
- package/templates/design-config.md +105 -0
- package/templates/design-doc.md +207 -0
- package/templates/epic.md +100 -0
- package/templates/feature-spec.md +181 -0
- package/templates/idea-canvas.md +47 -0
- package/templates/implementation-plan.md +159 -0
- package/templates/prd-template.md +86 -0
- package/templates/preamble.md +89 -0
- package/templates/project-readme.md +35 -0
- package/templates/quality-gates.md +230 -0
- package/templates/spartan-config.yaml +164 -0
- package/templates/user-interview.md +69 -0
- package/templates/validation-checklist.md +108 -0
- package/templates/workflow-backend-micronaut.md +409 -0
- package/templates/workflow-frontend-react.md +233 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Spartan AI Toolkit — Resolver Tests
|
|
2
|
+
// Run: node --test toolkit/lib/resolver.test.js
|
|
3
|
+
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { resolve, detectCycles, resolveAliases, toPacks, validatePack, loadExternalPacks } from './resolver.js';
|
|
7
|
+
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
// Helper: create a minimal manifest map
|
|
11
|
+
function makeManifests(defs) {
|
|
12
|
+
const m = new Map();
|
|
13
|
+
for (const [name, opts] of Object.entries(defs)) {
|
|
14
|
+
m.set(name, {
|
|
15
|
+
name,
|
|
16
|
+
description: `${name} pack`,
|
|
17
|
+
priority: opts.priority ?? 999,
|
|
18
|
+
hidden: opts.hidden ?? false,
|
|
19
|
+
depends: opts.depends ?? [],
|
|
20
|
+
commands: opts.commands ?? [],
|
|
21
|
+
rules: opts.rules ?? [],
|
|
22
|
+
skills: opts.skills ?? [],
|
|
23
|
+
agents: opts.agents ?? [],
|
|
24
|
+
'claude-sections': opts.claudeSections ?? [],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return m;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const testManifests = makeManifests({
|
|
31
|
+
core: { priority: 0 },
|
|
32
|
+
database: { priority: 5, hidden: true },
|
|
33
|
+
'shared-backend': { priority: 6, hidden: true },
|
|
34
|
+
'backend-micronaut': { priority: 11, depends: ['database', 'shared-backend'], commands: ['kotlin-service'] },
|
|
35
|
+
'frontend-react': { priority: 20 },
|
|
36
|
+
product: { priority: 40 },
|
|
37
|
+
research: { priority: 60, depends: ['product'] },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('resolve', () => {
|
|
41
|
+
it('basic resolve — backend-micronaut includes deps + core', () => {
|
|
42
|
+
const result = resolve(['backend-micronaut'], testManifests);
|
|
43
|
+
assert.ok(result.includes('core'));
|
|
44
|
+
assert.ok(result.includes('database'));
|
|
45
|
+
assert.ok(result.includes('shared-backend'));
|
|
46
|
+
assert.ok(result.includes('backend-micronaut'));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('no duplicates — two packs sharing a dep', () => {
|
|
50
|
+
// Both backend-micronaut and a hypothetical other pack could share database
|
|
51
|
+
const result = resolve(['backend-micronaut', 'database'], testManifests);
|
|
52
|
+
const counts = {};
|
|
53
|
+
for (const p of result) counts[p] = (counts[p] || 0) + 1;
|
|
54
|
+
for (const [name, count] of Object.entries(counts)) {
|
|
55
|
+
assert.equal(count, 1, `${name} appears ${count} times`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('priority ordering — resolved packs sorted by priority', () => {
|
|
60
|
+
const result = resolve(['backend-micronaut', 'research'], testManifests);
|
|
61
|
+
for (let i = 1; i < result.length; i++) {
|
|
62
|
+
const prevPriority = testManifests.get(result[i - 1])?.priority ?? 999;
|
|
63
|
+
const currPriority = testManifests.get(result[i])?.priority ?? 999;
|
|
64
|
+
assert.ok(prevPriority <= currPriority, `${result[i - 1]} (${prevPriority}) should come before ${result[i]} (${currPriority})`);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('core always first — no matter what you pick', () => {
|
|
69
|
+
const result = resolve(['research'], testManifests);
|
|
70
|
+
assert.equal(result[0], 'core');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('unknown pack — throws clear error', () => {
|
|
74
|
+
assert.throws(
|
|
75
|
+
() => resolve(['nonexistent'], testManifests),
|
|
76
|
+
{ message: "Unknown pack: 'nonexistent'" }
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('research pulls product as dependency', () => {
|
|
81
|
+
const result = resolve(['research'], testManifests);
|
|
82
|
+
assert.ok(result.includes('product'));
|
|
83
|
+
assert.ok(result.includes('research'));
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('detectCycles', () => {
|
|
88
|
+
it('no cycles — passes silently', () => {
|
|
89
|
+
assert.doesNotThrow(() => detectCycles(testManifests));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('cycle detection — A depends on B, B depends on A', () => {
|
|
93
|
+
const cyclic = makeManifests({
|
|
94
|
+
core: { priority: 0 },
|
|
95
|
+
a: { priority: 1, depends: ['b'] },
|
|
96
|
+
b: { priority: 2, depends: ['a'] },
|
|
97
|
+
});
|
|
98
|
+
assert.throws(
|
|
99
|
+
() => detectCycles(cyclic),
|
|
100
|
+
/Cycle detected/
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('resolveAliases', () => {
|
|
106
|
+
it('maps backend to backend-micronaut', () => {
|
|
107
|
+
const { resolved, warnings } = resolveAliases(['backend', 'product']);
|
|
108
|
+
assert.deepEqual(resolved, ['backend-micronaut', 'product']);
|
|
109
|
+
assert.equal(warnings.length, 1);
|
|
110
|
+
assert.ok(warnings[0].includes('backend-micronaut'));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('maps frontend to frontend-react', () => {
|
|
114
|
+
const { resolved } = resolveAliases(['frontend']);
|
|
115
|
+
assert.deepEqual(resolved, ['frontend-react']);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('passes through unknown names unchanged', () => {
|
|
119
|
+
const { resolved, warnings } = resolveAliases(['product', 'ops']);
|
|
120
|
+
assert.deepEqual(resolved, ['product', 'ops']);
|
|
121
|
+
assert.equal(warnings.length, 0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('toPacks', () => {
|
|
126
|
+
it('converts manifests to PACKS format', () => {
|
|
127
|
+
const { PACKS, PACK_ORDER } = toPacks(testManifests);
|
|
128
|
+
assert.ok(PACKS.core);
|
|
129
|
+
assert.ok(PACKS['backend-micronaut']);
|
|
130
|
+
assert.equal(PACKS['backend-micronaut'].description, 'backend-micronaut pack');
|
|
131
|
+
assert.deepEqual(PACKS['backend-micronaut'].depends, ['database', 'shared-backend']);
|
|
132
|
+
assert.equal(PACK_ORDER[0], 'core');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('PACK_ORDER sorted by priority', () => {
|
|
136
|
+
const { PACK_ORDER } = toPacks(testManifests);
|
|
137
|
+
assert.equal(PACK_ORDER[0], 'core');
|
|
138
|
+
const idx = (name) => PACK_ORDER.indexOf(name);
|
|
139
|
+
assert.ok(idx('database') < idx('backend-micronaut'));
|
|
140
|
+
assert.ok(idx('product') < idx('research'));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ── Test fixtures for validatePack / loadExternalPacks ──────────
|
|
145
|
+
const TMP = join(import.meta.dirname, '..', '.test-tmp-resolver');
|
|
146
|
+
|
|
147
|
+
function setupExternal() {
|
|
148
|
+
rmSync(TMP, { recursive: true, force: true });
|
|
149
|
+
mkdirSync(TMP, { recursive: true });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function cleanupExternal() {
|
|
153
|
+
rmSync(TMP, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
describe('validatePack', () => {
|
|
157
|
+
it('passes for a valid manifest', () => {
|
|
158
|
+
const manifest = {
|
|
159
|
+
name: 'my-pack',
|
|
160
|
+
description: 'A test pack',
|
|
161
|
+
commands: [],
|
|
162
|
+
rules: [],
|
|
163
|
+
skills: [],
|
|
164
|
+
agents: [],
|
|
165
|
+
};
|
|
166
|
+
const result = validatePack(manifest, TMP, new Set());
|
|
167
|
+
assert.ok(result.valid, `errors: ${result.errors.join(', ')}`);
|
|
168
|
+
assert.equal(result.errors.length, 0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('fails when name is missing', () => {
|
|
172
|
+
const manifest = { description: 'No name' };
|
|
173
|
+
const result = validatePack(manifest, TMP, new Set());
|
|
174
|
+
assert.ok(!result.valid);
|
|
175
|
+
assert.ok(result.errors.some(e => e.includes('name')));
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('fails when description is missing', () => {
|
|
179
|
+
const manifest = { name: 'no-desc' };
|
|
180
|
+
const result = validatePack(manifest, TMP, new Set());
|
|
181
|
+
assert.ok(!result.valid);
|
|
182
|
+
assert.ok(result.errors.some(e => e.includes('description')));
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('fails when name collides with built-in pack', () => {
|
|
186
|
+
const manifest = { name: 'core', description: 'Fake core' };
|
|
187
|
+
const builtins = new Set(['core', 'backend-micronaut']);
|
|
188
|
+
const result = validatePack(manifest, TMP, builtins);
|
|
189
|
+
assert.ok(!result.valid);
|
|
190
|
+
assert.ok(result.errors.some(e => e.includes('collides')));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('fails when name uses wrong format', () => {
|
|
194
|
+
const manifest = { name: 'My_Pack', description: 'Bad name' };
|
|
195
|
+
const result = validatePack(manifest, TMP, new Set());
|
|
196
|
+
assert.ok(!result.valid);
|
|
197
|
+
assert.ok(result.errors.some(e => e.includes('kebab-case')));
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('fails when dependency references unknown pack', () => {
|
|
201
|
+
const manifest = {
|
|
202
|
+
name: 'my-pack',
|
|
203
|
+
description: 'Has bad dep',
|
|
204
|
+
depends: ['nonexistent-pack'],
|
|
205
|
+
};
|
|
206
|
+
const builtins = new Set(['core']);
|
|
207
|
+
const result = validatePack(manifest, TMP, builtins);
|
|
208
|
+
assert.ok(!result.valid);
|
|
209
|
+
assert.ok(result.errors.some(e => e.includes('nonexistent-pack')));
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('allows dependency on built-in pack', () => {
|
|
213
|
+
const manifest = {
|
|
214
|
+
name: 'my-pack',
|
|
215
|
+
description: 'Depends on core',
|
|
216
|
+
depends: ['core'],
|
|
217
|
+
};
|
|
218
|
+
const builtins = new Set(['core']);
|
|
219
|
+
const result = validatePack(manifest, TMP, builtins);
|
|
220
|
+
assert.ok(result.valid, `errors: ${result.errors.join(', ')}`);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('warns about missing command files', () => {
|
|
224
|
+
setupExternal();
|
|
225
|
+
try {
|
|
226
|
+
const packDir = join(TMP, 'my-pack');
|
|
227
|
+
mkdirSync(join(packDir, 'commands', 'spartan'), { recursive: true });
|
|
228
|
+
// command file does NOT exist
|
|
229
|
+
const manifest = {
|
|
230
|
+
name: 'my-pack',
|
|
231
|
+
description: 'Missing command',
|
|
232
|
+
commands: ['nonexistent-cmd'],
|
|
233
|
+
};
|
|
234
|
+
const result = validatePack(manifest, packDir, new Set());
|
|
235
|
+
assert.ok(result.warnings.some(w => w.includes('nonexistent-cmd')));
|
|
236
|
+
} finally {
|
|
237
|
+
cleanupExternal();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('loadExternalPacks', () => {
|
|
243
|
+
it('loads valid external pack from directory', () => {
|
|
244
|
+
setupExternal();
|
|
245
|
+
try {
|
|
246
|
+
const packDir = join(TMP, 'community');
|
|
247
|
+
mkdirSync(join(packDir, 'packs'), { recursive: true });
|
|
248
|
+
writeFileSync(join(packDir, 'packs', 'go-backend.yaml'), `
|
|
249
|
+
name: go-backend
|
|
250
|
+
description: "Go backend rules"
|
|
251
|
+
category: Backend
|
|
252
|
+
priority: 100
|
|
253
|
+
commands: []
|
|
254
|
+
rules: []
|
|
255
|
+
skills: []
|
|
256
|
+
agents: []
|
|
257
|
+
claude-sections: []
|
|
258
|
+
`);
|
|
259
|
+
const builtins = new Set(['core']);
|
|
260
|
+
const result = loadExternalPacks(packDir, builtins);
|
|
261
|
+
assert.ok(result.loaded.has('go-backend'));
|
|
262
|
+
assert.equal(result.errors.length, 0);
|
|
263
|
+
} finally {
|
|
264
|
+
cleanupExternal();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('rejects external pack with name collision', () => {
|
|
269
|
+
setupExternal();
|
|
270
|
+
try {
|
|
271
|
+
const packDir = join(TMP, 'bad-community');
|
|
272
|
+
mkdirSync(join(packDir, 'packs'), { recursive: true });
|
|
273
|
+
writeFileSync(join(packDir, 'packs', 'core.yaml'), `
|
|
274
|
+
name: core
|
|
275
|
+
description: "Fake core"
|
|
276
|
+
`);
|
|
277
|
+
const builtins = new Set(['core']);
|
|
278
|
+
const result = loadExternalPacks(packDir, builtins);
|
|
279
|
+
assert.ok(!result.loaded.has('core'));
|
|
280
|
+
assert.ok(result.errors.length > 0);
|
|
281
|
+
} finally {
|
|
282
|
+
cleanupExternal();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('returns empty when directory has no packs/', () => {
|
|
287
|
+
setupExternal();
|
|
288
|
+
try {
|
|
289
|
+
const packDir = join(TMP, 'empty');
|
|
290
|
+
mkdirSync(packDir, { recursive: true });
|
|
291
|
+
const result = loadExternalPacks(packDir, new Set());
|
|
292
|
+
assert.equal(result.loaded.size, 0);
|
|
293
|
+
assert.equal(result.errors.length, 0);
|
|
294
|
+
} finally {
|
|
295
|
+
cleanupExternal();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
package/lib/worktree.sh
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Spartan worktree manager — reusable functions for isolated builds.
|
|
3
|
+
# Modeled after gstack's lib/worktree.ts WorktreeManager class.
|
|
4
|
+
#
|
|
5
|
+
# Usage in build commands:
|
|
6
|
+
# source ~/.spartan/lib/worktree.sh (if installed)
|
|
7
|
+
# spartan_worktree_create "my-feature"
|
|
8
|
+
# spartan_worktree_guard
|
|
9
|
+
# spartan_worktree_cleanup "my-feature"
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Create an isolated worktree for a feature build.
|
|
14
|
+
# Creates branch, symlinks planning dirs, copies .env.
|
|
15
|
+
#
|
|
16
|
+
# Args:
|
|
17
|
+
# $1 — slug (e.g., "user-auth-flow")
|
|
18
|
+
# $2 — branch prefix (default: "feature")
|
|
19
|
+
#
|
|
20
|
+
# Prints:
|
|
21
|
+
# WORKSPACE=<path>
|
|
22
|
+
# BRANCH=<name>
|
|
23
|
+
spartan_worktree_create() {
|
|
24
|
+
local slug="$1"
|
|
25
|
+
local prefix="${2:-feature}"
|
|
26
|
+
local branch="$prefix/$slug"
|
|
27
|
+
local main_repo
|
|
28
|
+
main_repo="$(git rev-parse --show-toplevel)"
|
|
29
|
+
local workspace="$main_repo/.worktrees/$slug"
|
|
30
|
+
|
|
31
|
+
if [ -d "$workspace" ]; then
|
|
32
|
+
echo "RESUMING: $workspace"
|
|
33
|
+
else
|
|
34
|
+
git worktree add "$workspace" -b "$branch" 2>/dev/null || \
|
|
35
|
+
git worktree add "$workspace" "$branch"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Symlink planning directories so worktree shares them with main repo
|
|
39
|
+
for dir in .planning .memory .handoff .spartan; do
|
|
40
|
+
[ -d "$main_repo/$dir" ] && [ ! -e "$workspace/$dir" ] && \
|
|
41
|
+
ln -s "$main_repo/$dir" "$workspace/$dir"
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
# Copy .env (not symlinked — worktree may need different values)
|
|
45
|
+
[ -f "$main_repo/.env" ] && [ ! -f "$workspace/.env" ] && \
|
|
46
|
+
cp "$main_repo/.env" "$workspace/.env"
|
|
47
|
+
|
|
48
|
+
# Ensure .worktrees/ is gitignored
|
|
49
|
+
grep -qxF '.worktrees/' "$main_repo/.gitignore" 2>/dev/null || \
|
|
50
|
+
echo '.worktrees/' >> "$main_repo/.gitignore"
|
|
51
|
+
|
|
52
|
+
echo "WORKSPACE=$workspace"
|
|
53
|
+
echo "BRANCH=$branch"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Guard: verify we're inside a worktree, not the main repo.
|
|
57
|
+
# Returns 0 if OK, 1 if in main repo (should stop).
|
|
58
|
+
spartan_worktree_guard() {
|
|
59
|
+
local main_repo
|
|
60
|
+
main_repo="$(git worktree list | head -1 | awk '{print $1}')"
|
|
61
|
+
local current
|
|
62
|
+
current="$(git rev-parse --show-toplevel)"
|
|
63
|
+
|
|
64
|
+
if [ "$main_repo" = "$current" ]; then
|
|
65
|
+
echo "ERROR: Working in main repo, not a worktree. Run spartan_worktree_create first."
|
|
66
|
+
return 1
|
|
67
|
+
else
|
|
68
|
+
echo "OK: Worktree at $current"
|
|
69
|
+
return 0
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Remove a worktree after PR is merged.
|
|
74
|
+
#
|
|
75
|
+
# Args:
|
|
76
|
+
# $1 — slug used during create
|
|
77
|
+
spartan_worktree_cleanup() {
|
|
78
|
+
local slug="$1"
|
|
79
|
+
local main_repo
|
|
80
|
+
main_repo="$(git worktree list | head -1 | awk '{print $1}')"
|
|
81
|
+
|
|
82
|
+
git -C "$main_repo" worktree remove ".worktrees/$slug" --force 2>/dev/null || true
|
|
83
|
+
git -C "$main_repo" worktree prune 2>/dev/null || true
|
|
84
|
+
echo "Cleaned up worktree: $slug"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# List all active worktrees.
|
|
88
|
+
spartan_worktree_list() {
|
|
89
|
+
git worktree list 2>/dev/null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Prune stale worktrees from previous runs.
|
|
93
|
+
spartan_worktree_prune() {
|
|
94
|
+
local main_repo
|
|
95
|
+
main_repo="$(git rev-parse --show-toplevel)"
|
|
96
|
+
git worktree prune 2>/dev/null || true
|
|
97
|
+
|
|
98
|
+
local worktree_base="$main_repo/.worktrees"
|
|
99
|
+
if [ -d "$worktree_base" ]; then
|
|
100
|
+
# Remove empty directories left behind
|
|
101
|
+
find "$worktree_base" -maxdepth 1 -type d -empty -delete 2>/dev/null || true
|
|
102
|
+
fi
|
|
103
|
+
echo "Pruned stale worktrees"
|
|
104
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@c0x12c/ai-toolkit",
|
|
3
|
+
"version": "1.15.0",
|
|
4
|
+
"description": "Engineering discipline layer for AI coding agents — commands, rules, skills, agents, and packs for Claude Code",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-toolkit": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"commands/",
|
|
13
|
+
"skills/",
|
|
14
|
+
"rules/",
|
|
15
|
+
"agents/",
|
|
16
|
+
"claude-md/",
|
|
17
|
+
"frameworks/",
|
|
18
|
+
"templates/",
|
|
19
|
+
"packs/",
|
|
20
|
+
"VERSION",
|
|
21
|
+
".claude-plugin/"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"claude-code",
|
|
25
|
+
"ai-toolkit",
|
|
26
|
+
"cursor",
|
|
27
|
+
"windsurf",
|
|
28
|
+
"codex",
|
|
29
|
+
"copilot",
|
|
30
|
+
"engineering",
|
|
31
|
+
"agent-skills"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/c0x12c/ai-toolkit"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node --test lib/resolver.test.js lib/assembler.test.js lib/detector.test.js",
|
|
40
|
+
"gen:codex": "node scripts/gen-codex-skills.js",
|
|
41
|
+
"check:codex": "node scripts/check-codex-skills.js",
|
|
42
|
+
"prepublishOnly": "node scripts/compile-packs.js && node scripts/gen-codex-skills.js"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"js-yaml": "^4.1.1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: backend-micronaut
|
|
2
|
+
description: "Kotlin + Micronaut backend"
|
|
3
|
+
category: Backend
|
|
4
|
+
priority: 11
|
|
5
|
+
hidden: false
|
|
6
|
+
depends:
|
|
7
|
+
- database
|
|
8
|
+
- shared-backend
|
|
9
|
+
|
|
10
|
+
commands:
|
|
11
|
+
- kotlin-service
|
|
12
|
+
- review
|
|
13
|
+
- testcontainer
|
|
14
|
+
|
|
15
|
+
rules:
|
|
16
|
+
- backend-micronaut/KOTLIN.md
|
|
17
|
+
- backend-micronaut/CONTROLLERS.md
|
|
18
|
+
- backend-micronaut/SERVICES_AND_BEANS.md
|
|
19
|
+
- backend-micronaut/API_DESIGN.md
|
|
20
|
+
- backend-micronaut/RETROFIT_PLACEMENT.md
|
|
21
|
+
- backend-micronaut/BATCH_PROCESSING.md
|
|
22
|
+
|
|
23
|
+
skills:
|
|
24
|
+
- api-endpoint-creator
|
|
25
|
+
- backend-api-design
|
|
26
|
+
- kotlin-best-practices
|
|
27
|
+
- testing-strategies
|
|
28
|
+
- security-checklist
|
|
29
|
+
|
|
30
|
+
agents:
|
|
31
|
+
- micronaut-backend-expert.md
|
|
32
|
+
- solution-architect-cto.md
|
|
33
|
+
|
|
34
|
+
claude-sections:
|
|
35
|
+
- 11-backend-micronaut.md
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: backend-nodejs
|
|
2
|
+
description: "Node.js backend (coming soon)"
|
|
3
|
+
category: Backend
|
|
4
|
+
priority: 12
|
|
5
|
+
hidden: false
|
|
6
|
+
coming-soon: true
|
|
7
|
+
depends:
|
|
8
|
+
- database
|
|
9
|
+
- shared-backend
|
|
10
|
+
|
|
11
|
+
commands: []
|
|
12
|
+
rules: []
|
|
13
|
+
skills: []
|
|
14
|
+
agents: []
|
|
15
|
+
claude-sections: []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: backend-python
|
|
2
|
+
description: "Python backend (coming soon)"
|
|
3
|
+
category: Backend
|
|
4
|
+
priority: 13
|
|
5
|
+
hidden: false
|
|
6
|
+
coming-soon: true
|
|
7
|
+
depends:
|
|
8
|
+
- database
|
|
9
|
+
- shared-backend
|
|
10
|
+
|
|
11
|
+
commands: []
|
|
12
|
+
rules: []
|
|
13
|
+
skills: []
|
|
14
|
+
agents: []
|
|
15
|
+
claude-sections: []
|
package/packs/core.yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: core
|
|
2
|
+
description: "Core workflow (always installed)"
|
|
3
|
+
category: Core
|
|
4
|
+
priority: 0
|
|
5
|
+
hidden: false
|
|
6
|
+
|
|
7
|
+
commands:
|
|
8
|
+
- spec
|
|
9
|
+
- plan
|
|
10
|
+
- build
|
|
11
|
+
- debug
|
|
12
|
+
- onboard
|
|
13
|
+
- daily
|
|
14
|
+
- context-save
|
|
15
|
+
- update
|
|
16
|
+
- pr-ready
|
|
17
|
+
- init-project
|
|
18
|
+
- init-rules
|
|
19
|
+
- scan-rules
|
|
20
|
+
- lint-rules
|
|
21
|
+
- careful
|
|
22
|
+
- freeze
|
|
23
|
+
- unfreeze
|
|
24
|
+
- guard
|
|
25
|
+
- sessions
|
|
26
|
+
- contribute
|
|
27
|
+
- gate-review
|
|
28
|
+
|
|
29
|
+
rules:
|
|
30
|
+
- core/NAMING_CONVENTIONS.md
|
|
31
|
+
- core/TIMEZONE.md
|
|
32
|
+
- core/SKILL_AUTHORING.md
|
|
33
|
+
|
|
34
|
+
skills: []
|
|
35
|
+
agents:
|
|
36
|
+
- phase-reviewer.md
|
|
37
|
+
claude-sections: []
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: database
|
|
2
|
+
description: "Database patterns, migrations, Exposed ORM"
|
|
3
|
+
category: Backend
|
|
4
|
+
priority: 5
|
|
5
|
+
hidden: true
|
|
6
|
+
|
|
7
|
+
commands:
|
|
8
|
+
- migration
|
|
9
|
+
|
|
10
|
+
rules:
|
|
11
|
+
- database/SCHEMA.md
|
|
12
|
+
- database/ORM_AND_REPO.md
|
|
13
|
+
- database/TRANSACTIONS.md
|
|
14
|
+
|
|
15
|
+
skills:
|
|
16
|
+
- database-patterns
|
|
17
|
+
- database-table-creator
|
|
18
|
+
|
|
19
|
+
agents: []
|
|
20
|
+
claude-sections:
|
|
21
|
+
- 05-database.md
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: frontend-react
|
|
2
|
+
description: "React + Next.js frontend"
|
|
3
|
+
category: Frontend
|
|
4
|
+
priority: 20
|
|
5
|
+
hidden: false
|
|
6
|
+
|
|
7
|
+
commands:
|
|
8
|
+
- next-app
|
|
9
|
+
- next-feature
|
|
10
|
+
- fe-review
|
|
11
|
+
- figma-to-code
|
|
12
|
+
- e2e
|
|
13
|
+
- qa
|
|
14
|
+
|
|
15
|
+
rules:
|
|
16
|
+
- frontend-react/FRONTEND.md
|
|
17
|
+
|
|
18
|
+
skills:
|
|
19
|
+
- browser-qa
|
|
20
|
+
|
|
21
|
+
agents: []
|
|
22
|
+
|
|
23
|
+
claude-sections:
|
|
24
|
+
- 20-frontend-react.md
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: infrastructure
|
|
2
|
+
description: "Terraform + AWS infrastructure"
|
|
3
|
+
category: Ops
|
|
4
|
+
priority: 30
|
|
5
|
+
hidden: false
|
|
6
|
+
depends: []
|
|
7
|
+
|
|
8
|
+
commands:
|
|
9
|
+
- tf-scaffold
|
|
10
|
+
- tf-module
|
|
11
|
+
- tf-review
|
|
12
|
+
- tf-plan
|
|
13
|
+
- tf-deploy
|
|
14
|
+
- tf-import
|
|
15
|
+
- tf-drift
|
|
16
|
+
- tf-cost
|
|
17
|
+
- tf-security
|
|
18
|
+
|
|
19
|
+
rules:
|
|
20
|
+
- infrastructure/STRUCTURE.md
|
|
21
|
+
- infrastructure/MODULES.md
|
|
22
|
+
- infrastructure/STATE_AND_BACKEND.md
|
|
23
|
+
- infrastructure/NAMING.md
|
|
24
|
+
- infrastructure/SECURITY.md
|
|
25
|
+
- infrastructure/VARIABLES.md
|
|
26
|
+
- infrastructure/PROVIDERS.md
|
|
27
|
+
|
|
28
|
+
skills:
|
|
29
|
+
- terraform-service-scaffold
|
|
30
|
+
- terraform-module-creator
|
|
31
|
+
- terraform-review
|
|
32
|
+
- terraform-security-audit
|
|
33
|
+
- terraform-best-practices
|
|
34
|
+
|
|
35
|
+
agents:
|
|
36
|
+
- infrastructure-expert.md
|
|
37
|
+
- sre-architect.md
|
|
38
|
+
|
|
39
|
+
claude-sections:
|
|
40
|
+
- 30-infrastructure.md
|