spacekid 0.0.0-stage → 0.1.5
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/bin/spacekid.js +6 -0
- package/package.json +51 -4
- package/src/cli.js +50 -0
- package/src/index.js +3 -0
- package/src/scaffold.js +68 -0
- package/src/templates/plans-agents.md +62 -0
- package/src/templates/root-agents.md +49 -0
- package/src/templates/specs-agents.md +55 -0
- package/types/index.d.ts +10 -0
- package/README.md +0 -3
package/bin/spacekid.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spacekid",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "A minimalist spec kit for agentic development.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"spec",
|
|
8
|
+
"agents",
|
|
9
|
+
"agentic-development",
|
|
10
|
+
"cli"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/victorradael/spacekid.git",
|
|
15
|
+
"directory": "js"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/victorradael/spacekid/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/victorradael/spacekid#readme",
|
|
21
|
+
"bin": {
|
|
22
|
+
"spacekid": "bin/spacekid.js"
|
|
23
|
+
},
|
|
24
|
+
"main": "src/index.js",
|
|
25
|
+
"types": "types/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./types/index.d.ts",
|
|
29
|
+
"default": "./src/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"bin",
|
|
35
|
+
"src",
|
|
36
|
+
"types"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=20.0.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@semantic-release/exec": "7.1.0",
|
|
46
|
+
"semantic-release": "25.0.9",
|
|
47
|
+
"semantic-release-monorepo": "8.0.2"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"provenance": true
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { parseArgs } = require('node:util');
|
|
5
|
+
|
|
6
|
+
const { scaffold } = require('./scaffold');
|
|
7
|
+
|
|
8
|
+
const STATUS_LABEL = {
|
|
9
|
+
created: 'created',
|
|
10
|
+
appended: 'appended to',
|
|
11
|
+
skipped: 'skipped (already exists)',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const USAGE = `usage: spacekid init [--path <dir>] [--name <name>]
|
|
15
|
+
|
|
16
|
+
init scaffold the SpaceKid AGENTS.md cycle in a project
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
function init(args) {
|
|
20
|
+
const { values } = parseArgs({
|
|
21
|
+
args,
|
|
22
|
+
options: {
|
|
23
|
+
path: { type: 'string', default: '.' },
|
|
24
|
+
name: { type: 'string' },
|
|
25
|
+
},
|
|
26
|
+
allowPositionals: false,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const projectRoot = path.resolve(values.path);
|
|
30
|
+
for (const result of scaffold(projectRoot, values.name)) {
|
|
31
|
+
console.log(`${STATUS_LABEL[result.status]}: ${path.relative(projectRoot, result.path)}`);
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function main(argv) {
|
|
37
|
+
const [command, ...rest] = argv;
|
|
38
|
+
if (command !== 'init') {
|
|
39
|
+
process.stderr.write(USAGE);
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return init(rest);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
process.stderr.write(`spacekid: ${err.message}\n${USAGE}`);
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { main };
|
package/src/index.js
ADDED
package/src/scaffold.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const APP_NAME_PLACEHOLDER = '{{APP_NAME}}';
|
|
7
|
+
const TEMPLATES_DIR = path.join(__dirname, 'templates');
|
|
8
|
+
|
|
9
|
+
function loadTemplate(filename) {
|
|
10
|
+
return fs.readFileSync(path.join(TEMPLATES_DIR, filename), 'utf8');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function detectAppName(projectRoot) {
|
|
14
|
+
const manifest = path.join(projectRoot, 'package.json');
|
|
15
|
+
if (fs.existsSync(manifest)) {
|
|
16
|
+
try {
|
|
17
|
+
const { name } = JSON.parse(fs.readFileSync(manifest, 'utf8'));
|
|
18
|
+
if (typeof name === 'string' && name.length > 0) {
|
|
19
|
+
return name;
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// Unreadable or malformed package.json: fall back to the directory name.
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return path.basename(path.resolve(projectRoot));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function renderRootAgents(appName) {
|
|
29
|
+
return loadTemplate('root-agents.md').split(APP_NAME_PLACEHOLDER).join(appName);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function writeOrSkip(filePath, content) {
|
|
33
|
+
if (fs.existsSync(filePath)) {
|
|
34
|
+
return { path: filePath, status: 'skipped' };
|
|
35
|
+
}
|
|
36
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
37
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
38
|
+
return { path: filePath, status: 'created' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function writeOrAppend(filePath, content) {
|
|
42
|
+
if (fs.existsSync(filePath)) {
|
|
43
|
+
fs.appendFileSync(filePath, '\n' + content, 'utf8');
|
|
44
|
+
return { path: filePath, status: 'appended' };
|
|
45
|
+
}
|
|
46
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
47
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
48
|
+
return { path: filePath, status: 'created' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function scaffold(projectRoot, appName) {
|
|
52
|
+
const root = path.resolve(projectRoot);
|
|
53
|
+
const resolvedName = appName || detectAppName(root);
|
|
54
|
+
|
|
55
|
+
return [
|
|
56
|
+
writeOrSkip(path.join(root, 'AGENTS.md'), renderRootAgents(resolvedName)),
|
|
57
|
+
writeOrAppend(path.join(root, 'specs', 'AGENTS.md'), loadTemplate('specs-agents.md')),
|
|
58
|
+
writeOrAppend(path.join(root, 'plans', 'AGENTS.md'), loadTemplate('plans-agents.md')),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
detectAppName,
|
|
64
|
+
renderRootAgents,
|
|
65
|
+
writeOrSkip,
|
|
66
|
+
writeOrAppend,
|
|
67
|
+
scaffold,
|
|
68
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# AGENTS.md — Writing Implementation Plans
|
|
2
|
+
|
|
3
|
+
A plan translates a spec into a **technical design**: how it will be built,
|
|
4
|
+
with the highest quality and best software engineering practices.
|
|
5
|
+
|
|
6
|
+
Follow the same writing discipline as `specs/AGENTS.md` — concise, clear,
|
|
7
|
+
no fluff — applied here to engineering rather than business content.
|
|
8
|
+
|
|
9
|
+
## Guiding principles
|
|
10
|
+
|
|
11
|
+
- **KISS.** The simplest design that correctly satisfies the spec wins.
|
|
12
|
+
Well-executed simplicity beats clever complexity every time.
|
|
13
|
+
- **SOLID.** Apply these principles where they add real value — not as
|
|
14
|
+
ceremony.
|
|
15
|
+
- **Scalability & maintainability.** Design for the load and change patterns
|
|
16
|
+
the spec actually implies, not speculative future needs. Don't build for
|
|
17
|
+
hypothetical requirements.
|
|
18
|
+
|
|
19
|
+
## Rules
|
|
20
|
+
|
|
21
|
+
- **One plan per spec**, named identically: `plans/<name>.md` implements
|
|
22
|
+
`specs/<name>.md`.
|
|
23
|
+
- **This file may reference its spec** — it should open by linking to
|
|
24
|
+
`specs/<name>.md`. This is the only direction references flow: specs never
|
|
25
|
+
reference plans.
|
|
26
|
+
- **English only.**
|
|
27
|
+
|
|
28
|
+
## Structure
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
# <Feature Name> — Implementation Plan
|
|
32
|
+
|
|
33
|
+
Implements: `specs/<name>.md`
|
|
34
|
+
Status: Draft | Approved | In Progress | Done
|
|
35
|
+
|
|
36
|
+
## Approach
|
|
37
|
+
The chosen technical approach and why it's the simplest one that satisfies
|
|
38
|
+
the spec.
|
|
39
|
+
|
|
40
|
+
## Steps
|
|
41
|
+
Ordered, concrete implementation steps.
|
|
42
|
+
|
|
43
|
+
## Key Decisions
|
|
44
|
+
Notable design/architecture choices and their trade-offs (only if non-obvious).
|
|
45
|
+
|
|
46
|
+
## Implementation Notes
|
|
47
|
+
Filled in after the work is done: what changed vs. the original plan, and
|
|
48
|
+
why (edge cases found, constraints discovered, simplifications made).
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## After implementation — mandatory
|
|
52
|
+
|
|
53
|
+
As soon as the implementation is finished:
|
|
54
|
+
|
|
55
|
+
1. Set `Status: Done`.
|
|
56
|
+
2. Fill in **Implementation Notes** with any deviation from the original plan
|
|
57
|
+
and the reason for it.
|
|
58
|
+
3. Go back to `specs/<name>.md` and update its status per `specs/AGENTS.md`.
|
|
59
|
+
4. If this work affected the behavior of any other already-implemented
|
|
60
|
+
spec/plan pair, update those too.
|
|
61
|
+
|
|
62
|
+
Never consider a task finished while this update step is pending.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Agent Guide — {{APP_NAME}}
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
<!--
|
|
6
|
+
AGENT TODO: This section is a placeholder. Replace it as soon as you have
|
|
7
|
+
enough context about this specific project — its purpose, its intended
|
|
8
|
+
users, and any key constraints or decisions that shape it. Do this the
|
|
9
|
+
first time you work on a real feature here, not before you actually know
|
|
10
|
+
the answers. Once this section reflects the real project, delete this
|
|
11
|
+
comment block; a placeholder left in place after that point is a bug in
|
|
12
|
+
this file, not a feature.
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
_Not yet described. Fill in what this project does, who it's for, and any
|
|
16
|
+
constraints that matter before writing the first spec._
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
|
|
20
|
+
This file orients any agent working in this repository. It maps where the rules live and defines the mandatory cycle that connects business intent, engineering planning, and implementation.
|
|
21
|
+
|
|
22
|
+
## Project Map
|
|
23
|
+
|
|
24
|
+
- `AGENTS.md` (this file) — the map, the cycle, and this project's overview.
|
|
25
|
+
- `specs/AGENTS.md` — how to write a spec. Read this before writing or editing anything in `specs/`.
|
|
26
|
+
- `specs/*.md` — one spec per feature. Business-only, self-contained.
|
|
27
|
+
- `plans/AGENTS.md` — how to write an implementation plan. Read this before writing or editing anything in `plans/`.
|
|
28
|
+
- `plans/*.md` — one implementation plan per feature, paired with its spec by matching slug (e.g. `specs/foo-spec.md` ↔ `plans/foo-plan.md`).
|
|
29
|
+
- This project's own architecture or coding-convention guide, if one has been added — read it before writing or editing implementation code.
|
|
30
|
+
|
|
31
|
+
## The Cycle
|
|
32
|
+
|
|
33
|
+
Every non-trivial feature must flow through these steps, in order:
|
|
34
|
+
|
|
35
|
+
1. **Spec.** Write or update a spec in `specs/`, following `specs/AGENTS.md`. It captures the business problem, what must be delivered, and acceptance criteria — nothing about how.
|
|
36
|
+
2. **Plan.** Write or update an implementation plan in `plans/`, following `plans/AGENTS.md`. It references the spec and lays out the engineering approach.
|
|
37
|
+
3. **Implementation.** Build the feature following the plan and this project's own conventions.
|
|
38
|
+
4. **Close the loop.** As soon as implementation finishes:
|
|
39
|
+
- Update the plan's Status and its Adaptations Log with anything that changed from the original plan during the build.
|
|
40
|
+
- Update the spec's Status to reflect that it is now implemented.
|
|
41
|
+
5. **Stay current.** If later work touches an area already covered by an existing spec or plan — even when that wasn't the original goal of the task — revisit and update those files too, so they never go stale.
|
|
42
|
+
|
|
43
|
+
## Rules Agents Must Never Break
|
|
44
|
+
|
|
45
|
+
- Never implement a non-trivial feature without a spec in `specs/` and a plan in `plans/` already in place.
|
|
46
|
+
- Never finish an implementation task without updating the Status of both the plan and its spec.
|
|
47
|
+
- Never let a spec or plan go stale after a related change lands elsewhere in the codebase — step 5 is not optional.
|
|
48
|
+
- Never write a spec that contains implementation details, or a plan that skips referencing its spec.
|
|
49
|
+
- Never leave the Project Overview placeholder above unfilled once the project's purpose is actually known.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# AGENTS.md — Writing Specs
|
|
2
|
+
|
|
3
|
+
A spec captures the **business intent** of a feature: why it exists, what it
|
|
4
|
+
must do, and how we know it's done. Nothing more.
|
|
5
|
+
|
|
6
|
+
## Rules
|
|
7
|
+
|
|
8
|
+
- **Self-contained.** A reader must understand the full spec without opening
|
|
9
|
+
any other file. Do not reference other specs, plans, code files, or paths.
|
|
10
|
+
- **No implementation detail.** No code, no technology choices, no
|
|
11
|
+
architecture, no data models. That belongs exclusively in the matching
|
|
12
|
+
`plans/<name>.md`.
|
|
13
|
+
- **Concise and clear.** Say only what's needed to preserve the core idea.
|
|
14
|
+
Prefer short sentences and bullet lists over long prose. Cut anything that
|
|
15
|
+
doesn't change what gets built or how it's accepted.
|
|
16
|
+
- **Business-only scope.** A spec centralizes business points: context,
|
|
17
|
+
problem, what must be delivered, and acceptance criteria. It does not
|
|
18
|
+
belong to engineering.
|
|
19
|
+
- **English only.**
|
|
20
|
+
|
|
21
|
+
## Structure
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
# <Feature Name>
|
|
25
|
+
|
|
26
|
+
Status: Draft | Approved | Implemented
|
|
27
|
+
|
|
28
|
+
## Context
|
|
29
|
+
Why this is needed. The problem or opportunity, in business terms.
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
What must exist or happen once this is delivered. Plain, unambiguous
|
|
33
|
+
statements — no solution design.
|
|
34
|
+
|
|
35
|
+
## Acceptance Criteria
|
|
36
|
+
A checklist of verifiable conditions that define "done".
|
|
37
|
+
|
|
38
|
+
## Out of Scope
|
|
39
|
+
What this spec explicitly does not cover (optional, only if it prevents
|
|
40
|
+
ambiguity).
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What happens next
|
|
44
|
+
|
|
45
|
+
The implementation plan for this spec is written in `plans/<name>.md`
|
|
46
|
+
(same `<name>`), following `plans/AGENTS.md`. The plan file is the only place
|
|
47
|
+
allowed to reference this spec — this spec must never reference the plan or
|
|
48
|
+
any code.
|
|
49
|
+
|
|
50
|
+
## Status updates
|
|
51
|
+
|
|
52
|
+
Once the feature is implemented, the `Status` field here must be updated to
|
|
53
|
+
`Implemented`. If real-world constraints forced any acceptance criteria to
|
|
54
|
+
change during implementation, update this file to reflect what was actually
|
|
55
|
+
delivered.
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface FileResult {
|
|
2
|
+
path: string;
|
|
3
|
+
status: 'created' | 'appended' | 'skipped';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function detectAppName(projectRoot: string): string;
|
|
7
|
+
export function renderRootAgents(appName: string): string;
|
|
8
|
+
export function writeOrSkip(filePath: string, content: string): FileResult;
|
|
9
|
+
export function writeOrAppend(filePath: string, content: string): FileResult;
|
|
10
|
+
export function scaffold(projectRoot: string, appName?: string): FileResult[];
|
package/README.md
DELETED