ctxflow 1.0.0 → 1.0.1
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 +172 -0
- package/bin/ctxflow.js +4 -1
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +61 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +32 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +2 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +36 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +46 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/CLAUDE.md.hbs +47 -0
- package/dist/templates/approved-libs.md.hbs +34 -0
- package/dist/templates/architecture.md.hbs +42 -0
- package/dist/templates/decisions.md.hbs +26 -0
- package/dist/templates/plan.md.hbs +24 -0
- package/dist/templates/project-decisions.md.hbs +27 -0
- package/dist/templates/requirements.md.hbs +35 -0
- package/dist/ui/banner.d.ts +2 -0
- package/dist/ui/banner.d.ts.map +1 -0
- package/dist/ui/banner.js +22 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/menu.d.ts +3 -0
- package/dist/ui/menu.d.ts.map +1 -0
- package/dist/ui/menu.js +222 -0
- package/dist/ui/menu.js.map +1 -0
- package/dist/utils/fs.d.ts +11 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +40 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +10 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +29 -3
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
```
|
|
2
|
+
██████╗████████╗██╗ ██╗███████╗██╗ ██████╗ ██╗ ██╗
|
|
3
|
+
██╔════╝╚══██╔══╝╚██╗██╔╝██╔════╝██║ ██╔═══██╗██║ ██║
|
|
4
|
+
██║ ██║ ╚███╔╝ █████╗ ██║ ██║ ██║██║ █╗ ██║
|
|
5
|
+
██║ ██║ ██╔██╗ ██╔══╝ ██║ ██║ ██║██║███╗██║
|
|
6
|
+
╚██████╗ ██║ ██╔╝ ██╗██║ ███████╗╚██████╔╝╚███╔███╔╝
|
|
7
|
+
╚═════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<strong>Context scaffolding for AI agent-driven development.</strong><br/>
|
|
12
|
+
<sub>Give your AI the context it needs — before it starts guessing.</sub>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://img.shields.io/npm/v/ctxflow?color=a855f7&label=npm" alt="npm version"/>
|
|
17
|
+
<img src="https://img.shields.io/node/v/ctxflow?color=06b6d4&label=node" alt="node version"/>
|
|
18
|
+
<img src="https://img.shields.io/npm/l/ctxflow?color=a855f7" alt="license"/>
|
|
19
|
+
<img src="https://img.shields.io/npm/dm/ctxflow?color=06b6d4" alt="downloads"/>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## The problem
|
|
25
|
+
|
|
26
|
+
You open your AI coding assistant and ask it to implement a feature.
|
|
27
|
+
It starts writing code — but it doesn't know your stack, your constraints, your architecture decisions, or what's already been agreed upon.
|
|
28
|
+
|
|
29
|
+
So it guesses. And you spend the next hour correcting it.
|
|
30
|
+
|
|
31
|
+
**ctxflow fixes this.**
|
|
32
|
+
|
|
33
|
+
It scaffolds structured context files your AI can read before touching a single line of code: requirements, task plans, architectural decisions, and project-wide rules — all in plain Markdown, all version-controlled alongside your code.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g ctxflow
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 1. Initialise your project (once)
|
|
49
|
+
ctxflow init
|
|
50
|
+
|
|
51
|
+
# 2. Create a feature context before you start coding
|
|
52
|
+
ctxflow new user-authentication
|
|
53
|
+
|
|
54
|
+
# 3. Fill in requirements.md — your AI reads this first
|
|
55
|
+
# 4. Check progress at any time
|
|
56
|
+
ctxflow status user-authentication
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or just run `ctxflow` for the full interactive menu.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## What gets created
|
|
64
|
+
|
|
65
|
+
### `ctxflow init`
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
CLAUDE.md ← AI instructions: stack, architecture, approvals, rules
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**CLAUDE.md** is the single source of truth your AI assistant reads on every session.
|
|
72
|
+
It contains your stack, architecture overview, approved libraries, and non-negotiable rules.
|
|
73
|
+
|
|
74
|
+
### `ctxflow new <feature>`
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
docs/features/<feature>/
|
|
78
|
+
requirements.md ← Why this feature exists, constraints, acceptance criteria
|
|
79
|
+
plan.md ← Tasks (checkboxes), file structure, open questions
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Interactive menu
|
|
85
|
+
|
|
86
|
+
Run `ctxflow` with no arguments for a full interactive experience:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
What would you like to do?
|
|
90
|
+
|
|
91
|
+
[1] New feature — scaffold a new feature context
|
|
92
|
+
[2] Init project — create CLAUDE.md + /docs structure
|
|
93
|
+
[3] List features — show all existing plans
|
|
94
|
+
[4] Status — check tasks in a feature plan
|
|
95
|
+
[5] Help — show usage and documentation
|
|
96
|
+
|
|
97
|
+
Ctrl+C to exit
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Press a number key to execute instantly — no Enter needed.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## CLI reference
|
|
105
|
+
|
|
106
|
+
| Command | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `ctxflow` | Launch interactive menu |
|
|
109
|
+
| `ctxflow init` | Scaffold `CLAUDE.md` + `docs/` in the current directory |
|
|
110
|
+
| `ctxflow new <feature>` | Create a feature context under `docs/features/<feature>/` |
|
|
111
|
+
| `ctxflow list` | List all feature contexts with creation dates |
|
|
112
|
+
| `ctxflow status <feature>` | Show task completion progress for a feature |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## The workflow
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
1. ctxflow init → Write CLAUDE.md once. Define your stack, rules, boundaries.
|
|
120
|
+
2. ctxflow new <feature> → Before every feature: scaffold requirements + plan.
|
|
121
|
+
3. Fill requirements.md → Context, constraints, acceptance criteria. Your AI reads this.
|
|
122
|
+
4. Code the feature → AI works with full context. No guessing.
|
|
123
|
+
5. Check tasks off → Mark [ ] → [x] in plan.md as you go.
|
|
124
|
+
6. ctxflow status → See exactly where you are at any time.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Why this works
|
|
130
|
+
|
|
131
|
+
AI coding assistants are only as useful as the context they receive. Without structured context:
|
|
132
|
+
|
|
133
|
+
| Without ctxflow | With ctxflow |
|
|
134
|
+
|----------------|--------------|
|
|
135
|
+
| Agent guesses at your stack | Agent reads your exact stack from `CLAUDE.md` |
|
|
136
|
+
| Unapproved libraries sneak in | `CLAUDE.md` lists exactly what's approved |
|
|
137
|
+
| Requirements drift mid-feature | `requirements.md` is the single source of truth |
|
|
138
|
+
| Agent asks you the same questions every session | Context is version-controlled and always available |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Works with any AI assistant
|
|
143
|
+
|
|
144
|
+
ctxflow is **assistant-agnostic**. The generated files are plain Markdown that any AI can read:
|
|
145
|
+
|
|
146
|
+
- **Claude** (reads `CLAUDE.md` natively)
|
|
147
|
+
- **GitHub Copilot**
|
|
148
|
+
- **Cursor**
|
|
149
|
+
- **Windsurf**
|
|
150
|
+
- **Codeium**
|
|
151
|
+
- Any tool that supports reading project files
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Works with any language or framework
|
|
156
|
+
|
|
157
|
+
ctxflow generates language-agnostic context files.
|
|
158
|
+
Whether you're building in Java, Python, Go, Rust, TypeScript, or Ruby —
|
|
159
|
+
fill in your stack, and your AI gets the right context.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Requirements
|
|
164
|
+
|
|
165
|
+
- Node.js 18+
|
|
166
|
+
- Works on macOS, Linux, Windows
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT © ctxflow contributors
|
package/bin/ctxflow.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAqCA,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBvE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { readTemplate, renderTemplate, writeFile, pathExists } from '../utils/fs.js';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
const TODAY = new Date().toISOString().split('T')[0];
|
|
6
|
+
async function confirmOverwrite() {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const stdin = process.stdin;
|
|
9
|
+
process.stdout.write(` ${chalk.yellow('CLAUDE.md already exists.')} ${chalk.white('Overwrite?')}` +
|
|
10
|
+
chalk.gray(' Enter = yes / Esc = no '));
|
|
11
|
+
if (!stdin.isTTY) {
|
|
12
|
+
stdin.once('data', (c) => resolve(c.toString().trim().toLowerCase() === 'y'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
stdin.setRawMode(true);
|
|
16
|
+
stdin.resume();
|
|
17
|
+
stdin.once('data', (chunk) => {
|
|
18
|
+
stdin.setRawMode(false);
|
|
19
|
+
stdin.pause();
|
|
20
|
+
const key = chunk.toString();
|
|
21
|
+
process.stdout.write('\n');
|
|
22
|
+
if (key === '\x03')
|
|
23
|
+
process.exit(0); // Ctrl+C → exit
|
|
24
|
+
if (key === '\x1b') {
|
|
25
|
+
resolve(false);
|
|
26
|
+
return;
|
|
27
|
+
} // Esc → no
|
|
28
|
+
if (key === '\r' || key === '\n') {
|
|
29
|
+
resolve(true);
|
|
30
|
+
return;
|
|
31
|
+
} // Enter → yes
|
|
32
|
+
if (key.toLowerCase() === 'y') {
|
|
33
|
+
resolve(true);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
resolve(false); // anything else → no
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Returns true = completed, false = aborted (Esc / no)
|
|
41
|
+
export async function initCommand(cwd = process.cwd()) {
|
|
42
|
+
const claudePath = join(cwd, 'CLAUDE.md');
|
|
43
|
+
if (await pathExists(claudePath)) {
|
|
44
|
+
const yes = await confirmOverwrite();
|
|
45
|
+
if (!yes) {
|
|
46
|
+
console.log('');
|
|
47
|
+
logger.warn('Aborted — nothing changed.');
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const vars = { date: TODAY };
|
|
52
|
+
const claudeTpl = await readTemplate('CLAUDE.md.hbs');
|
|
53
|
+
await writeFile(claudePath, renderTemplate(claudeTpl, vars));
|
|
54
|
+
logger.success('Project initialised!');
|
|
55
|
+
logger.blank();
|
|
56
|
+
logger.file('CLAUDE.md');
|
|
57
|
+
logger.blank();
|
|
58
|
+
logger.info('Edit CLAUDE.md to fill in your stack, architecture, and approvals.');
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCpE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { readDir, statFile, pathExists } from '../utils/fs.js';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
export async function listCommand(cwd = process.cwd()) {
|
|
6
|
+
const plansDir = join(cwd, 'docs', 'features');
|
|
7
|
+
if (!(await pathExists(plansDir))) {
|
|
8
|
+
logger.warn('No docs/features directory found. Run `ctxflow init` first.');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const features = await readDir(plansDir);
|
|
12
|
+
if (features.length === 0) {
|
|
13
|
+
logger.info('No features yet. Run `ctxflow new <feature>` to create one.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const rows = await Promise.all(features.map(async (name) => {
|
|
17
|
+
const stat = await statFile(join(plansDir, name, 'plan.md'));
|
|
18
|
+
const created = stat ? stat.birthtime.toISOString().split('T')[0] : '—';
|
|
19
|
+
return { name, created };
|
|
20
|
+
}));
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(chalk.magenta(' Feature').padEnd(36) +
|
|
23
|
+
chalk.magenta('Created'));
|
|
24
|
+
console.log(' ' + chalk.gray('─'.repeat(46)));
|
|
25
|
+
for (const row of rows) {
|
|
26
|
+
console.log(chalk.cyan(' ' + row.name).padEnd(38) +
|
|
27
|
+
chalk.gray(row.created));
|
|
28
|
+
}
|
|
29
|
+
console.log('');
|
|
30
|
+
logger.info(`${rows.length} feature${rows.length === 1 ? '' : 's'} found.`);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAE/C,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CACzB,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE/C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CACxB,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAUA,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCxF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { readTemplate, renderTemplate, writeFile, pathExists } from '../utils/fs.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
const TODAY = new Date().toISOString().split('T')[0];
|
|
5
|
+
function slugify(name) {
|
|
6
|
+
return name.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
7
|
+
}
|
|
8
|
+
export async function newCommand(featureName, cwd = process.cwd()) {
|
|
9
|
+
const slug = slugify(featureName);
|
|
10
|
+
if (!slug) {
|
|
11
|
+
logger.error('Invalid feature name.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const dir = join(cwd, 'docs', 'features', slug);
|
|
15
|
+
if (await pathExists(dir)) {
|
|
16
|
+
logger.warn(`Feature "${slug}" already exists at docs/features/${slug}`);
|
|
17
|
+
logger.info('Edit the existing files or choose a different name.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const vars = { feature: featureName.trim(), date: TODAY };
|
|
21
|
+
const [reqTpl, planTpl] = await Promise.all([
|
|
22
|
+
readTemplate('requirements.md.hbs'),
|
|
23
|
+
readTemplate('plan.md.hbs'),
|
|
24
|
+
]);
|
|
25
|
+
await Promise.all([
|
|
26
|
+
writeFile(join(dir, 'requirements.md'), renderTemplate(reqTpl, vars)),
|
|
27
|
+
writeFile(join(dir, 'plan.md'), renderTemplate(planTpl, vars)),
|
|
28
|
+
]);
|
|
29
|
+
logger.success(`Feature "${featureName.trim()}" created!`);
|
|
30
|
+
logger.blank();
|
|
31
|
+
logger.file(`docs/features/${slug}/requirements.md`);
|
|
32
|
+
logger.file(`docs/features/${slug}/plan.md`);
|
|
33
|
+
logger.blank();
|
|
34
|
+
logger.info('Start by filling in requirements.md before writing any code.');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAErD,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAEhD,IAAI,MAAM,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,qCAAqC,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAE1D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC1C,YAAY,CAAC,qBAAqB,CAAC;QACnC,YAAY,CAAC,aAAa,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,cAAc,CAAC,MAAM,EAAG,IAAI,CAAC,CAAC;QACtE,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAU,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;KACvE,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,YAAY,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,kBAAkB,CAAC,CAAC;IACrD,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,UAAU,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,MAAM,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAoBA,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC3F"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { pathExists, readFileContent } from '../utils/fs.js';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
function parseTasks(content) {
|
|
6
|
+
const tasks = [];
|
|
7
|
+
const pattern = /^- \[([xX ])\] (.+)$/gm;
|
|
8
|
+
let match;
|
|
9
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
10
|
+
tasks.push({ done: match[1].toLowerCase() === 'x', text: match[2] });
|
|
11
|
+
}
|
|
12
|
+
return tasks;
|
|
13
|
+
}
|
|
14
|
+
export async function statusCommand(featureName, cwd = process.cwd()) {
|
|
15
|
+
const planPath = join(cwd, 'docs', 'features', featureName, 'plan.md');
|
|
16
|
+
if (!(await pathExists(planPath))) {
|
|
17
|
+
logger.error(`Feature "${featureName}" not found at docs/features/${featureName}/plan.md`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const content = await readFileContent(planPath);
|
|
21
|
+
const tasks = parseTasks(content);
|
|
22
|
+
if (tasks.length === 0) {
|
|
23
|
+
logger.warn('No tasks found in plan.md (no `- [ ]` or `- [x]` lines).');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const done = tasks.filter(t => t.done).length;
|
|
27
|
+
const total = tasks.length;
|
|
28
|
+
const percent = Math.round((done / total) * 100);
|
|
29
|
+
const bar = buildBar(done, total);
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(chalk.cyan(` ${featureName}`));
|
|
32
|
+
console.log(` ${bar} ${chalk.white(`${done}/${total}`)} ${chalk.gray(`(${percent}%)`)}`);
|
|
33
|
+
console.log('');
|
|
34
|
+
for (const task of tasks) {
|
|
35
|
+
const icon = task.done ? chalk.green('✅') : chalk.gray('⬜');
|
|
36
|
+
const label = task.done ? chalk.gray(task.text) : chalk.white(task.text);
|
|
37
|
+
console.log(` ${icon} ${label}`);
|
|
38
|
+
}
|
|
39
|
+
console.log('');
|
|
40
|
+
}
|
|
41
|
+
function buildBar(done, total, width = 20) {
|
|
42
|
+
const filled = Math.round((done / total) * width);
|
|
43
|
+
const empty = width - filled;
|
|
44
|
+
return chalk.cyan('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Command, CommanderError } from 'commander';
|
|
2
|
+
import { showBanner } from './ui/banner.js';
|
|
3
|
+
import { showMenu, showHelp } from './ui/menu.js';
|
|
4
|
+
import { initCommand } from './commands/init.js';
|
|
5
|
+
import { newCommand } from './commands/new.js';
|
|
6
|
+
import { listCommand } from './commands/list.js';
|
|
7
|
+
import { statusCommand } from './commands/status.js';
|
|
8
|
+
import { logger } from './utils/logger.js';
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name('ctxflow')
|
|
12
|
+
.description('Context scaffolding for AI agent-driven development')
|
|
13
|
+
.version('1.0.0')
|
|
14
|
+
.exitOverride()
|
|
15
|
+
.configureOutput({ outputError: () => { } }); // suppress commander's raw error lines
|
|
16
|
+
program
|
|
17
|
+
.command('init')
|
|
18
|
+
.description('Create CLAUDE.md and docs/ structure in the current project')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
showBanner();
|
|
21
|
+
await initCommand();
|
|
22
|
+
});
|
|
23
|
+
program
|
|
24
|
+
.command('new <feature>')
|
|
25
|
+
.description('Scaffold docs/plans/<feature>/ with requirements, plan, and decisions files')
|
|
26
|
+
.action(async (feature) => {
|
|
27
|
+
showBanner();
|
|
28
|
+
await newCommand(feature);
|
|
29
|
+
});
|
|
30
|
+
program
|
|
31
|
+
.command('list')
|
|
32
|
+
.description('List all feature plans in docs/plans/')
|
|
33
|
+
.action(async () => {
|
|
34
|
+
showBanner();
|
|
35
|
+
await listCommand();
|
|
36
|
+
});
|
|
37
|
+
program
|
|
38
|
+
.command('status <feature>')
|
|
39
|
+
.description('Show task completion status for a feature plan')
|
|
40
|
+
.action(async (feature) => {
|
|
41
|
+
showBanner();
|
|
42
|
+
await statusCommand(feature);
|
|
43
|
+
});
|
|
44
|
+
// Unknown command (no dash) → show help
|
|
45
|
+
program.on('command:*', () => {
|
|
46
|
+
showBanner();
|
|
47
|
+
showHelp();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
if (process.argv.length <= 2) {
|
|
51
|
+
showMenu().catch((err) => {
|
|
52
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
58
|
+
if (err instanceof CommanderError) {
|
|
59
|
+
// --version / --help already printed their output cleanly
|
|
60
|
+
if (err.exitCode === 0)
|
|
61
|
+
process.exit(0);
|
|
62
|
+
// Unknown option (dashes) → show help
|
|
63
|
+
showBanner();
|
|
64
|
+
showHelp();
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
> This file is read by AI coding assistants (Claude, Copilot, etc.) to understand the project context. Keep it accurate and up to date.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- Language: <!-- e.g. Java 21, Python 3.12, Go 1.23, Node.js 22 -->
|
|
8
|
+
- Framework: <!-- e.g. Spring Boot, Django, Gin, Express -->
|
|
9
|
+
- Testing: <!-- e.g. JUnit 5, pytest, Go test, Vitest -->
|
|
10
|
+
|
|
11
|
+
## Project structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/
|
|
15
|
+
├── src/ # Source code
|
|
16
|
+
├── tests/ # Test files
|
|
17
|
+
├── docs/
|
|
18
|
+
│ └── features/ # Feature context (managed by ctxflow)
|
|
19
|
+
└── CLAUDE.md # This file
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
<!-- Describe the high-level architecture: main components, data flow, key boundaries.
|
|
25
|
+
Update this when structure changes. -->
|
|
26
|
+
|
|
27
|
+
## Approvals
|
|
28
|
+
|
|
29
|
+
<!-- Authoritative list of approved dependencies. Check before adding anything new. -->
|
|
30
|
+
|
|
31
|
+
| Library / Tool | Version | Purpose |
|
|
32
|
+
|---------------|---------|---------|
|
|
33
|
+
| | | |
|
|
34
|
+
|
|
35
|
+
## Rules (non-negotiable)
|
|
36
|
+
|
|
37
|
+
1. Read `docs/features/<feature>/requirements.md` BEFORE implementing any feature.
|
|
38
|
+
2. Write tests before marking a task as complete.
|
|
39
|
+
3. Check **## Approvals** before adding any new dependency.
|
|
40
|
+
4. Keep this file updated when the stack or rules change.
|
|
41
|
+
|
|
42
|
+
## What NOT to do
|
|
43
|
+
|
|
44
|
+
- Do NOT add libraries not in **## Approvals** without updating it first.
|
|
45
|
+
- Do NOT commit to main/master directly — use feature branches and code review.
|
|
46
|
+
- Do NOT skip requirements.md when starting a feature.
|
|
47
|
+
- Do NOT change architecture without updating **## Architecture** in this file.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Approved libraries
|
|
2
|
+
|
|
3
|
+
> Last updated: {{date}}
|
|
4
|
+
>
|
|
5
|
+
> Authoritative list of approved dependencies.
|
|
6
|
+
> Before adding any library, update this file and get it reviewed.
|
|
7
|
+
|
|
8
|
+
## Production dependencies
|
|
9
|
+
|
|
10
|
+
| Library | Version | Purpose | Added by |
|
|
11
|
+
|---------|---------|---------|---------|
|
|
12
|
+
| | | | |
|
|
13
|
+
|
|
14
|
+
## Development dependencies
|
|
15
|
+
|
|
16
|
+
| Library | Version | Purpose | Added by |
|
|
17
|
+
|---------|---------|---------|---------|
|
|
18
|
+
| | | | |
|
|
19
|
+
|
|
20
|
+
## Evaluation criteria
|
|
21
|
+
|
|
22
|
+
Before approving a new library:
|
|
23
|
+
|
|
24
|
+
1. Actively maintained? (last commit < 6 months)
|
|
25
|
+
2. TypeScript support?
|
|
26
|
+
3. License compatible with this project?
|
|
27
|
+
4. Acceptable bundle size impact?
|
|
28
|
+
5. Does an already-approved library cover this need?
|
|
29
|
+
|
|
30
|
+
## Rejected libraries
|
|
31
|
+
|
|
32
|
+
| Library | Reason | Alternative |
|
|
33
|
+
|---------|--------|------------|
|
|
34
|
+
| | | |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
> Last updated: {{date}}
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
<!-- High-level description of the system: what it does, who uses it, how it's deployed -->
|
|
8
|
+
|
|
9
|
+
## Components
|
|
10
|
+
|
|
11
|
+
<!-- Main modules / services and their responsibilities -->
|
|
12
|
+
|
|
13
|
+
### Component 1
|
|
14
|
+
|
|
15
|
+
- **Purpose:**
|
|
16
|
+
- **Technology:**
|
|
17
|
+
- **Exposes:**
|
|
18
|
+
|
|
19
|
+
## Data flow
|
|
20
|
+
|
|
21
|
+
<!-- How does data move through the system? (narrative or Mermaid diagram) -->
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph TD
|
|
25
|
+
A[Client] --> B[API Layer]
|
|
26
|
+
B --> C[Business Logic]
|
|
27
|
+
C --> D[Database]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Key decisions
|
|
31
|
+
|
|
32
|
+
<!-- Link to feature decisions.md files for major choices -->
|
|
33
|
+
|
|
34
|
+
- See `docs/plans/<feature>/decisions.md`
|
|
35
|
+
|
|
36
|
+
## External dependencies
|
|
37
|
+
|
|
38
|
+
<!-- Third-party services, APIs, or infrastructure this system relies on -->
|
|
39
|
+
|
|
40
|
+
| Service | Purpose | Owner |
|
|
41
|
+
|---------|---------|-------|
|
|
42
|
+
| | | |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Decisions log: {{feature}}
|
|
2
|
+
|
|
3
|
+
> Log every significant architectural or technical decision here.
|
|
4
|
+
> One entry per decision. Add to the top (newest first).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## {{date}} — [Decision title]
|
|
9
|
+
|
|
10
|
+
**Context:** What problem were you solving, and what constraints existed?
|
|
11
|
+
|
|
12
|
+
**Decision:** What did you decide to do?
|
|
13
|
+
|
|
14
|
+
**Alternatives considered:**
|
|
15
|
+
|
|
16
|
+
1. Option A — why rejected
|
|
17
|
+
2. Option B — why rejected
|
|
18
|
+
|
|
19
|
+
**Consequences:**
|
|
20
|
+
|
|
21
|
+
- ✅ Benefit or positive consequence
|
|
22
|
+
- ⚠️ Trade-off or risk to monitor
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<!-- Copy the block above for each new decision -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Plan: {{feature}}
|
|
2
|
+
|
|
3
|
+
> Created: {{date}}
|
|
4
|
+
> Status: In progress
|
|
5
|
+
|
|
6
|
+
## Tasks
|
|
7
|
+
|
|
8
|
+
<!-- Add tasks specific to this feature -->
|
|
9
|
+
|
|
10
|
+
- [ ]
|
|
11
|
+
|
|
12
|
+
## File structure
|
|
13
|
+
|
|
14
|
+
<!-- Files to create or modify for this feature -->
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{{feature}}/
|
|
18
|
+
├──
|
|
19
|
+
└──
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Notes
|
|
23
|
+
|
|
24
|
+
<!-- Open questions, blockers, or context for the AI agent -->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Project decisions log
|
|
2
|
+
|
|
3
|
+
> Project-wide architectural and technical decisions.
|
|
4
|
+
> Feature-specific decisions live in `docs/features/<feature>/decisions.md`.
|
|
5
|
+
> Add entries here for cross-cutting choices that affect the whole codebase.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## {{date}} — [Decision title]
|
|
10
|
+
|
|
11
|
+
**Context:** What problem were you solving, and what constraints existed?
|
|
12
|
+
|
|
13
|
+
**Decision:** What did you decide to do?
|
|
14
|
+
|
|
15
|
+
**Alternatives considered:**
|
|
16
|
+
|
|
17
|
+
1. Option A — why rejected
|
|
18
|
+
2. Option B — why rejected
|
|
19
|
+
|
|
20
|
+
**Consequences:**
|
|
21
|
+
|
|
22
|
+
- ✅ Benefit or positive consequence
|
|
23
|
+
- ⚠️ Trade-off or risk to monitor
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<!-- Copy the block above for each new project-level decision -->
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Requirements: {{feature}}
|
|
2
|
+
|
|
3
|
+
> Created: {{date}}
|
|
4
|
+
> Status: Draft
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
<!-- Why is this feature needed? What user problem does it solve? What is the business motivation? -->
|
|
9
|
+
|
|
10
|
+
## Stack
|
|
11
|
+
|
|
12
|
+
<!-- Which parts of the stack does this feature touch? -->
|
|
13
|
+
|
|
14
|
+
-
|
|
15
|
+
-
|
|
16
|
+
|
|
17
|
+
## Constraints
|
|
18
|
+
|
|
19
|
+
<!-- Technical, time, or business constraints that affect implementation -->
|
|
20
|
+
|
|
21
|
+
-
|
|
22
|
+
-
|
|
23
|
+
|
|
24
|
+
## Out of scope
|
|
25
|
+
|
|
26
|
+
<!-- What is explicitly NOT part of this feature (to avoid scope creep) -->
|
|
27
|
+
|
|
28
|
+
-
|
|
29
|
+
-
|
|
30
|
+
|
|
31
|
+
## Acceptance criteria
|
|
32
|
+
|
|
33
|
+
- [ ]
|
|
34
|
+
- [ ]
|
|
35
|
+
- [ ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,IAAI,IAAI,CAiBjC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import figlet from 'figlet';
|
|
2
|
+
import gradient from 'gradient-string';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
const PURPLE_CYAN = ['#9B59B6', '#7C3AED', '#2563EB', '#06B6D4'];
|
|
5
|
+
export function showBanner() {
|
|
6
|
+
let art;
|
|
7
|
+
try {
|
|
8
|
+
art = figlet.textSync('CTXFLOW', { font: 'ANSI Shadow' });
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
art = figlet.textSync('CTXFLOW', { font: 'Big' });
|
|
12
|
+
}
|
|
13
|
+
const g = gradient(PURPLE_CYAN);
|
|
14
|
+
const sep = gradient(['#9B59B6', '#06B6D4'])('─'.repeat(60));
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log(g.multiline(art));
|
|
17
|
+
console.log(chalk.cyan(' Context Scaffolding for AI Agent Development'));
|
|
18
|
+
console.log(chalk.gray(' v1.0.0'));
|
|
19
|
+
console.log(` ${sep}`);
|
|
20
|
+
console.log('');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,QAAQ,MAAM,iBAAiB,CAAC;AACvC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAEjE,MAAM,UAAU,UAAU;IACxB,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/ui/menu.ts"],"names":[],"mappings":"AAuJA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB9C;AA+DD,wBAAgB,QAAQ,IAAI,IAAI,CAU/B"}
|
package/dist/ui/menu.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { showBanner } from './banner.js';
|
|
3
|
+
import { initCommand } from '../commands/init.js';
|
|
4
|
+
import { newCommand } from '../commands/new.js';
|
|
5
|
+
import { listCommand } from '../commands/list.js';
|
|
6
|
+
import { statusCommand } from '../commands/status.js';
|
|
7
|
+
import { readDir, pathExists } from '../utils/fs.js';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
const MAIN_CHOICES = [
|
|
10
|
+
{ key: '1', label: 'New feature', desc: '— scaffold a new feature context', value: 'new' },
|
|
11
|
+
{ key: '2', label: 'Init project', desc: '— create CLAUDE.md + /docs structure', value: 'init' },
|
|
12
|
+
{ key: '3', label: 'List features', desc: '— show all existing plans', value: 'list' },
|
|
13
|
+
{ key: '4', label: 'Status', desc: '— check tasks in a feature plan', value: 'status' },
|
|
14
|
+
{ key: '5', label: 'Help', desc: '— show usage and documentation', value: 'help' },
|
|
15
|
+
];
|
|
16
|
+
// ─── raw input primitives ────────────────────────────────────────────────────
|
|
17
|
+
async function pressKey() {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
const stdin = process.stdin;
|
|
20
|
+
if (!stdin.isTTY) {
|
|
21
|
+
stdin.once('data', (c) => resolve(c.toString().trim()[0] ?? ''));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
stdin.setRawMode(true);
|
|
25
|
+
stdin.resume();
|
|
26
|
+
stdin.once('data', (chunk) => {
|
|
27
|
+
const key = chunk.toString();
|
|
28
|
+
stdin.setRawMode(false);
|
|
29
|
+
stdin.pause();
|
|
30
|
+
if (key === '\x03')
|
|
31
|
+
process.exit(0); // Ctrl+C always exits
|
|
32
|
+
resolve(key);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Custom text input: Esc → null, Ctrl+C → exit, Enter (non-empty) → string
|
|
37
|
+
async function readLine(prompt) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const stdin = process.stdin;
|
|
40
|
+
let buf = '';
|
|
41
|
+
process.stdout.write(prompt);
|
|
42
|
+
if (!stdin.isTTY) {
|
|
43
|
+
stdin.once('data', (c) => {
|
|
44
|
+
const t = c.toString().split('\n')[0]?.trim() ?? '';
|
|
45
|
+
resolve(t.length > 0 ? t : null);
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
stdin.setRawMode(true);
|
|
50
|
+
stdin.resume();
|
|
51
|
+
const cleanup = () => {
|
|
52
|
+
stdin.setRawMode(false);
|
|
53
|
+
stdin.pause();
|
|
54
|
+
stdin.removeListener('data', onData);
|
|
55
|
+
};
|
|
56
|
+
const onData = (chunk) => {
|
|
57
|
+
const key = chunk.toString();
|
|
58
|
+
if (key === '\x03') { // Ctrl+C → exit
|
|
59
|
+
cleanup();
|
|
60
|
+
process.stdout.write('\n');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
if (key === '\x1b') { // Esc → go back
|
|
64
|
+
cleanup();
|
|
65
|
+
process.stdout.write('\n');
|
|
66
|
+
resolve(null);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (key.startsWith('\x1b'))
|
|
70
|
+
return; // arrow keys / other sequences → ignore
|
|
71
|
+
if (key === '\r' || key === '\n') { // Enter
|
|
72
|
+
if (buf.trim().length === 0)
|
|
73
|
+
return; // empty → do nothing
|
|
74
|
+
cleanup();
|
|
75
|
+
process.stdout.write('\n');
|
|
76
|
+
resolve(buf.trim());
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key === '\x7f' || key === '\b') { // Backspace
|
|
80
|
+
if (buf.length > 0) {
|
|
81
|
+
buf = buf.slice(0, -1);
|
|
82
|
+
process.stdout.write('\b \b');
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (key.charCodeAt(0) >= 32) { // printable char
|
|
87
|
+
buf += key;
|
|
88
|
+
process.stdout.write(key);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
stdin.on('data', onData);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// ─── menu rendering ──────────────────────────────────────────────────────────
|
|
95
|
+
function renderChoices(message, choices, hint) {
|
|
96
|
+
console.log(chalk.cyan(` ${message}\n`));
|
|
97
|
+
for (const c of choices) {
|
|
98
|
+
const k = chalk.magenta(`[${c.key}]`);
|
|
99
|
+
const l = chalk.white(c.label.padEnd(16));
|
|
100
|
+
const d = chalk.gray(c.desc);
|
|
101
|
+
console.log(` ${k} ${l}${d}`);
|
|
102
|
+
}
|
|
103
|
+
if (hint)
|
|
104
|
+
console.log(`\n ${chalk.gray(hint)}`);
|
|
105
|
+
console.log('');
|
|
106
|
+
}
|
|
107
|
+
async function pickFromList(message, choices, opts = {}) {
|
|
108
|
+
const valid = new Set(choices.map(c => c.key));
|
|
109
|
+
renderChoices(message, choices, opts.hint);
|
|
110
|
+
while (true) {
|
|
111
|
+
const key = await pressKey();
|
|
112
|
+
if (key === '\x1b') {
|
|
113
|
+
if (opts.escBack) {
|
|
114
|
+
console.log('');
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
continue; // main menu: ignore Esc
|
|
118
|
+
}
|
|
119
|
+
if (!valid.has(key))
|
|
120
|
+
continue;
|
|
121
|
+
const choice = choices.find(c => c.key === key);
|
|
122
|
+
console.log(chalk.gray(` ${choice.label}`));
|
|
123
|
+
console.log('');
|
|
124
|
+
return choice.value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ─── public menu loop ────────────────────────────────────────────────────────
|
|
128
|
+
async function pressAnyKey() {
|
|
129
|
+
console.log(chalk.gray('\n Press any key to return to menu...'));
|
|
130
|
+
await pressKey();
|
|
131
|
+
}
|
|
132
|
+
export async function showMenu() {
|
|
133
|
+
let needsWait = false;
|
|
134
|
+
while (true) {
|
|
135
|
+
if (needsWait)
|
|
136
|
+
await pressAnyKey();
|
|
137
|
+
showBanner();
|
|
138
|
+
const action = await pickFromList('What would you like to do?', MAIN_CHOICES, { hint: 'Ctrl+C to exit' });
|
|
139
|
+
if (action === null) {
|
|
140
|
+
needsWait = false;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
console.log('');
|
|
144
|
+
let result = 'done';
|
|
145
|
+
switch (action) {
|
|
146
|
+
case 'new':
|
|
147
|
+
result = await handleNew();
|
|
148
|
+
break;
|
|
149
|
+
case 'init':
|
|
150
|
+
result = await handleInit();
|
|
151
|
+
break;
|
|
152
|
+
case 'list':
|
|
153
|
+
await listCommand();
|
|
154
|
+
break;
|
|
155
|
+
case 'status':
|
|
156
|
+
result = await handleStatus();
|
|
157
|
+
break;
|
|
158
|
+
case 'help':
|
|
159
|
+
showHelp();
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
needsWait = result === 'done';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// ─── sub-flows ───────────────────────────────────────────────────────────────
|
|
166
|
+
async function handleNew() {
|
|
167
|
+
const name = await readLine(` ${chalk.cyan('?')} ${chalk.white('Feature name')} ${chalk.gray('(Esc to go back)')} `);
|
|
168
|
+
if (name === null)
|
|
169
|
+
return 'back'; // Esc → skip wait, re-render immediately
|
|
170
|
+
console.log('');
|
|
171
|
+
await newCommand(name);
|
|
172
|
+
return 'done';
|
|
173
|
+
}
|
|
174
|
+
async function handleInit() {
|
|
175
|
+
const completed = await initCommand();
|
|
176
|
+
return completed ? 'done' : 'back';
|
|
177
|
+
}
|
|
178
|
+
async function handleStatus() {
|
|
179
|
+
const cwd = process.cwd();
|
|
180
|
+
const claudePath = join(cwd, 'CLAUDE.md');
|
|
181
|
+
const featuresDir = join(cwd, 'docs', 'features');
|
|
182
|
+
if (!(await pathExists(claudePath))) {
|
|
183
|
+
console.log(chalk.yellow(' ⚠ ctxflow not initialised here. Run `ctxflow init` first.'));
|
|
184
|
+
return 'done';
|
|
185
|
+
}
|
|
186
|
+
const features = await readDir(featuresDir);
|
|
187
|
+
if (features.length === 0) {
|
|
188
|
+
console.log(chalk.green(' ✓ ctxflow is active.') + chalk.gray(' No features added yet.'));
|
|
189
|
+
console.log(chalk.gray(' Run `ctxflow new <feature>` to create your first one.'));
|
|
190
|
+
return 'done';
|
|
191
|
+
}
|
|
192
|
+
console.log(chalk.green(' ✓ ctxflow is active.') +
|
|
193
|
+
chalk.gray(` ${features.length} feature${features.length === 1 ? '' : 's'} found.\n`));
|
|
194
|
+
const KEYS = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
195
|
+
const slice = features.slice(0, 9);
|
|
196
|
+
if (features.length > 9) {
|
|
197
|
+
console.log(chalk.cyan(' ℹ Showing first 9. Use `ctxflow status <name>` for the rest.\n'));
|
|
198
|
+
}
|
|
199
|
+
const choices = slice.map((f, i) => ({
|
|
200
|
+
key: KEYS[i],
|
|
201
|
+
label: f,
|
|
202
|
+
desc: '',
|
|
203
|
+
value: f,
|
|
204
|
+
}));
|
|
205
|
+
const feature = await pickFromList('Which feature?', choices, { escBack: true });
|
|
206
|
+
if (!feature)
|
|
207
|
+
return 'back'; // Esc → skip wait
|
|
208
|
+
await statusCommand(feature);
|
|
209
|
+
return 'done';
|
|
210
|
+
}
|
|
211
|
+
export function showHelp() {
|
|
212
|
+
console.log(chalk.cyan(' Usage'));
|
|
213
|
+
console.log('');
|
|
214
|
+
console.log(` ${chalk.magenta('ctxflow')} ${chalk.gray('→ show interactive menu')}`);
|
|
215
|
+
console.log(` ${chalk.magenta('ctxflow init')} ${chalk.gray('→ create CLAUDE.md + docs/ structure')}`);
|
|
216
|
+
console.log(` ${chalk.magenta('ctxflow new')} ${chalk.cyan('<feature>')} ${chalk.gray('→ scaffold docs/features/<feature>/')}`);
|
|
217
|
+
console.log(` ${chalk.magenta('ctxflow list')} ${chalk.gray('→ list all features')}`);
|
|
218
|
+
console.log(` ${chalk.magenta('ctxflow status')} ${chalk.cyan('<feature>')} ${chalk.gray('→ show task progress for a feature')}`);
|
|
219
|
+
console.log('');
|
|
220
|
+
console.log('');
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/ui/menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAQ,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAS,oBAAoB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAQ,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,MAAM,YAAY,GAAyB;IACzC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAI,IAAI,EAAE,kCAAkC,EAAK,KAAK,EAAE,KAAK,EAAK;IAClG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,EAAG,IAAI,EAAE,sCAAsC,EAAE,KAAK,EAAE,MAAM,EAAI;IACnG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,2BAA2B,EAAa,KAAK,EAAE,MAAM,EAAI;IACnG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAS,IAAI,EAAE,iCAAiC,EAAO,KAAK,EAAE,QAAQ,EAAE;IACnG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAW,IAAI,EAAE,gCAAgC,EAAQ,KAAK,EAAE,MAAM,EAAI;CACpG,CAAC;AAEF,gFAAgF;AAEhF,KAAK,UAAU,QAAQ;IACrB,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC7B,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACxB,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,GAAG,KAAK,MAAM;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB;YAC3D,OAAO,CAAC,GAAG,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,KAAK,UAAU,QAAQ,CAAC,MAAc;IACpC,OAAO,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;QAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,GAAG,GAAG,EAAE,CAAC;QAEb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE7B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;gBACvB,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBACpD,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,CAAC,MAAM,EAAE,CAAC;QAEf,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACxB,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;YACrC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAE7B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC,CAAkB,gBAAgB;gBACrD,OAAO,EAAE,CAAC;gBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC;YACD,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC,CAAkB,gBAAgB;gBACrD,OAAO,EAAE,CAAC;gBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO;YAC/D,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAI,wCAAwC;YAE/E,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC,CAAI,QAAQ;gBAC7C,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,CAAC,qBAAqB;gBAC1D,OAAO,EAAE,CAAC;gBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBAAC,OAAO;YACrE,CAAC;YACD,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC,CAAE,YAAY;gBACjD,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAAC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;oBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAAC,CAAC;gBAC9E,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAS,iBAAiB;gBACtD,GAAG,IAAI,GAAG,CAAC;gBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAEhF,SAAS,aAAa,CACpB,OAAe,EACf,OAAoB,EACpB,IAAa;IAEb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAOD,KAAK,UAAU,YAAY,CACzB,OAAe,EACf,OAAoB,EACpB,OAAiB,EAAE;IAEnB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC7B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC;YACnD,SAAS,CAAC,wBAAwB;QACpC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAc,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,KAAK,UAAU,WAAW;IACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC,CAAC;IAClE,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,SAAS;YAAE,MAAM,WAAW,EAAE,CAAC;QAEnC,UAAU,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,4BAA4B,EAC5B,YAAY,EACZ,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAC3B,CAAC;QAEF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAAC,SAAS,GAAG,KAAK,CAAC;YAAC,SAAS;QAAC,CAAC;QAErD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,IAAI,MAAM,GAAoB,MAAM,CAAC;QACrC,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,KAAK;gBAAK,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;gBAAC,MAAM;YACjD,KAAK,MAAM;gBAAI,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;gBAAC,MAAM;YAClD,KAAK,MAAM;gBAAI,MAAM,WAAW,EAAE,CAAC;gBAAC,MAAM;YAC1C,KAAK,QAAQ;gBAAE,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;gBAAC,MAAM;YACpD,KAAK,MAAM;gBAAI,QAAQ,EAAE,CAAC;gBAAC,MAAM;QACnC,CAAC;QACD,SAAS,GAAG,MAAM,KAAK,MAAM,CAAC;IAChC,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,KAAK,UAAU,SAAS;IACtB,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAC3F,CAAC;IACF,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,CAAC,yCAAyC;IAC3E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,MAAM,SAAS,GAAG,MAAM,WAAW,EAAE,CAAC;IACtC,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,MAAM,GAAG,GAAW,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,UAAU,GAAI,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAElD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,6DAA6D,CAAC,CAAC,CAAC;QACzF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IAE5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAC5F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC,CAAC;QACrF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,MAAM,WAAW,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CACvF,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,EAAC,GAAG,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEnC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,OAAO,GAAqB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACrD,GAAG,EAAE,IAAI,CAAC,CAAC,CAAW;QACtB,KAAK,EAAE,CAAC;QACR,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,CAAC;KACT,CAAC,CAAC,CAAC;IAEJ,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACjF,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,CAAC,kBAAkB;IAE/C,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,wBAAwB,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;IAC1G,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,mBAAmB,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,EAAE,CAAC,CAAC;IACvH,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,EAAE,CAAC,CAAC;IACpI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,mBAAmB,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;IACtG,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,EAAE,CAAC,CAAC;IACnI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const TEMPLATES_DIR: string;
|
|
2
|
+
export declare function renderTemplate(template: string, vars: Record<string, string>): string;
|
|
3
|
+
export declare function readTemplate(name: string): Promise<string>;
|
|
4
|
+
export declare function writeFile(filePath: string, content: string): Promise<void>;
|
|
5
|
+
export declare function pathExists(filePath: string): Promise<boolean>;
|
|
6
|
+
export declare function readDir(dirPath: string): Promise<string[]>;
|
|
7
|
+
export declare function readFileContent(filePath: string): Promise<string>;
|
|
8
|
+
export declare function statFile(filePath: string): Promise<{
|
|
9
|
+
birthtime: Date;
|
|
10
|
+
} | null>;
|
|
11
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,aAAa,QAAqC,CAAC;AAEhE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAErF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhF;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnE;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOhE;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvE;AAED,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC,CAMpF"}
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fsExtra from 'fs-extra';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = dirname(__filename);
|
|
6
|
+
export const TEMPLATES_DIR = join(__dirname, '..', 'templates');
|
|
7
|
+
export function renderTemplate(template, vars) {
|
|
8
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
9
|
+
}
|
|
10
|
+
export async function readTemplate(name) {
|
|
11
|
+
return fsExtra.readFile(join(TEMPLATES_DIR, name), 'utf-8');
|
|
12
|
+
}
|
|
13
|
+
export async function writeFile(filePath, content) {
|
|
14
|
+
await fsExtra.ensureDir(dirname(filePath));
|
|
15
|
+
await fsExtra.writeFile(filePath, content, 'utf-8');
|
|
16
|
+
}
|
|
17
|
+
export async function pathExists(filePath) {
|
|
18
|
+
return fsExtra.pathExists(filePath);
|
|
19
|
+
}
|
|
20
|
+
export async function readDir(dirPath) {
|
|
21
|
+
try {
|
|
22
|
+
const entries = await fsExtra.readdir(dirPath, { withFileTypes: true });
|
|
23
|
+
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function readFileContent(filePath) {
|
|
30
|
+
return fsExtra.readFile(filePath, 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
export async function statFile(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return await fsExtra.stat(filePath);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAEhE,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,IAA4B;IAC3E,OAAO,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,OAAe;IAC/D,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,MAAM,OAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,OAAO,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;mBACF,MAAM;iBACN,MAAM;gBACN,MAAM;gBACN,MAAM;iBACL,MAAM;;CAEvB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const logger = {
|
|
3
|
+
success: (msg) => console.log(chalk.green('✓') + ' ' + chalk.white(msg)),
|
|
4
|
+
error: (msg) => console.log(chalk.red('✗') + ' ' + chalk.red(msg)),
|
|
5
|
+
warn: (msg) => console.log(chalk.yellow('⚠') + ' ' + chalk.yellow(msg)),
|
|
6
|
+
info: (msg) => console.log(chalk.cyan('ℹ') + ' ' + chalk.white(msg)),
|
|
7
|
+
file: (path) => console.log(chalk.magenta(' →') + ' ' + chalk.gray(path)),
|
|
8
|
+
blank: () => console.log(''),
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChF,KAAK,EAAI,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5E,IAAI,EAAK,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClF,IAAI,EAAK,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/E,IAAI,EAAK,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,KAAK,EAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;CAC/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxflow",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Context scaffolding for AI
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Context scaffolding for AI agent-driven development",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
7
|
"ctxflow": "bin/ctxflow.js"
|
|
7
8
|
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc && node scripts/post-build.mjs",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node bin/ctxflow.js"
|
|
14
|
+
},
|
|
8
15
|
"keywords": [
|
|
9
16
|
"ai",
|
|
10
17
|
"context",
|
|
@@ -12,5 +19,24 @@
|
|
|
12
19
|
"claude",
|
|
13
20
|
"agents"
|
|
14
21
|
],
|
|
15
|
-
"license": "MIT"
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"files": ["bin", "dist"],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"chalk": "^5.3.0",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"figlet": "^1.7.0",
|
|
31
|
+
"fs-extra": "^11.2.0",
|
|
32
|
+
"gradient-string": "^3.0.0",
|
|
33
|
+
"inquirer": "^9.2.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/figlet": "^1.5.8",
|
|
37
|
+
"@types/fs-extra": "^11.0.4",
|
|
38
|
+
"@types/inquirer": "^9.0.7",
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"typescript": "^5.4.0"
|
|
41
|
+
}
|
|
16
42
|
}
|