@ksoftm/create-arc 1.0.7 → 1.1.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/README.md +11 -5
- package/bin/cli.js +123 -9
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
Scaffold and drive the **ARC framework** (Align → Refine → Construct) — plan-driven development for AI agents, in pure Markdown. Zero dependencies, any language, any agent.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
npm i -g @ksoftm/create-arc # install once, then use the short `arc` command
|
|
7
|
+
arc init # scaffold ARC into the current project
|
|
8
|
+
arc new "Add per-key rate limit" # open and register a new unit of work
|
|
9
|
+
arc status # every arc at a glance
|
|
10
|
+
arc doctor # verify the registry is consistent
|
|
11
|
+
arc agent-init # write /arc-* slash commands for your AI agent
|
|
10
12
|
```
|
|
11
13
|
|
|
14
|
+
No install? Use `npx @ksoftm/create-arc <command>`. Prefer pinning per-project? `npm i -D @ksoftm/create-arc` then `npx arc <command>`.
|
|
15
|
+
|
|
12
16
|
## What ARC is
|
|
13
17
|
|
|
14
18
|
Every unit of development is an **arc**: one Markdown file under `.arc/` that holds its whole story — the user's raw instructions (verbatim, append-only), the current plan, a refinement log, a task list, a worklog, and a status. The agent plans before it builds, refines the plan when the ask changes, and logs every edit — so any later session resumes from the arc, not the chat history. Full concept and protocol: **https://github.com/KsoftmHub/arc-framework-v1**.
|
|
@@ -32,7 +36,9 @@ Requires Node ≥ 18.
|
|
|
32
36
|
| `status [dir] [--json]` | Print a table (or JSON) of every arc: ID, status, plan version, task progress, and which to resume. |
|
|
33
37
|
| `doctor [dir]` | Consistency checks — index ↔ file bijection, ID/`next_id` sanity, valid statuses. Exits non-zero on problems (CI-friendly). |
|
|
34
38
|
|
|
35
|
-
Common options: `--owner NAME` (defaults to `git config user.name`); `--tags a,b` on `new`; `--json` on `status`; `--version`, `--help`.
|
|
39
|
+
Common options: `--owner NAME` (defaults to `git config user.name`); `--tags a,b` on `new`; `--json` on `status`; `--agents a,b` and `--force` on `agent-init`; `--version`, `--help`.
|
|
40
|
+
|
|
41
|
+
Installs two equivalent binaries: **`arc`** and **`create-arc`**.
|
|
36
42
|
|
|
37
43
|
## What `init` produces
|
|
38
44
|
|
package/bin/cli.js
CHANGED
|
@@ -166,12 +166,41 @@ function cmdNew(title, flags) {
|
|
|
166
166
|
index = index.replace(/^next_id:\s*ARC-\d+\s*$/m, `next_id: ARC-${String(num + 1).padStart(4, "0")}`);
|
|
167
167
|
const row = `| ${id} | ${title} | draft | 1 | ${date} | — | [${filename}](${filename}) |`;
|
|
168
168
|
const lines = index.split("\n");
|
|
169
|
+
const archivedIdx = lines.findIndex((l) => /^##\s+Archived/i.test(l));
|
|
170
|
+
const scanEnd = archivedIdx === -1 ? lines.length : archivedIdx;
|
|
171
|
+
|
|
172
|
+
// Preferred: insert right after the last existing "| ARC-…" row in the Active section.
|
|
169
173
|
let insertAt = -1;
|
|
170
|
-
for (let i = 0; i <
|
|
171
|
-
if (lines[i].startsWith("## Archived")) break;
|
|
174
|
+
for (let i = 0; i < scanEnd; i++) {
|
|
172
175
|
if (/^\|\s*ARC-/.test(lines[i])) insertAt = i;
|
|
173
176
|
}
|
|
174
|
-
|
|
177
|
+
|
|
178
|
+
if (insertAt === -1) {
|
|
179
|
+
// No arc rows yet (or a differently-shaped INDEX). Fall back, in order, to:
|
|
180
|
+
// the "## Active" heading's table separator, the heading itself, or next_id.
|
|
181
|
+
const activeIdx = lines.findIndex((l) => /^##\s+Active/i.test(l));
|
|
182
|
+
if (activeIdx !== -1) {
|
|
183
|
+
// find a markdown table separator (|---|) after the Active heading
|
|
184
|
+
let sep = -1;
|
|
185
|
+
for (let i = activeIdx + 1; i < scanEnd; i++) {
|
|
186
|
+
if (/^\|?\s*:?-{2,}/.test(lines[i]) || /^\|(\s*-+\s*\|)+/.test(lines[i])) { sep = i; break; }
|
|
187
|
+
if (/^\|\s*ID\s*\|/i.test(lines[i])) { sep = i + 1; } // header row → insert after its separator
|
|
188
|
+
}
|
|
189
|
+
insertAt = sep !== -1 ? sep : activeIdx; // after separator, else right after heading
|
|
190
|
+
} else {
|
|
191
|
+
// No Active section at all — synthesize one after next_id (or at top).
|
|
192
|
+
const nextIdIdx = lines.findIndex((l) => /^next_id:/.test(l));
|
|
193
|
+
const anchor = nextIdIdx !== -1 ? nextIdIdx : 0;
|
|
194
|
+
lines.splice(anchor + 1, 0,
|
|
195
|
+
"",
|
|
196
|
+
"## Active",
|
|
197
|
+
"",
|
|
198
|
+
"| ID | Title | Status | Plan v | Updated | Depends | File |",
|
|
199
|
+
"|---|---|---|---|---|---|---|");
|
|
200
|
+
insertAt = anchor + 5; // the separator line we just added
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
175
204
|
lines.splice(insertAt + 1, 0, row);
|
|
176
205
|
writeFileSync(indexPath, lines.join("\n"));
|
|
177
206
|
|
|
@@ -297,15 +326,99 @@ function cmdDoctor(target) {
|
|
|
297
326
|
|
|
298
327
|
function fail(msg) { console.error(`error: ${msg}`); return 1; }
|
|
299
328
|
|
|
329
|
+
// Slash-commands generated for each supported agent. Each is a thin prompt that
|
|
330
|
+
// routes the agent through ARC.md; $ARGUMENTS / $1 carry the user's text.
|
|
331
|
+
const ARC_COMMANDS = {
|
|
332
|
+
"arc-new": {
|
|
333
|
+
desc: "ARC: capture a development instruction as a new arc (Align)",
|
|
334
|
+
body: `Read ./ARC.md, then run ARC Intake for this instruction:
|
|
335
|
+
|
|
336
|
+
"$ARGUMENTS"
|
|
337
|
+
|
|
338
|
+
Steps: read .arc/INDEX.md; if an open arc already covers this, append the instruction verbatim and refine its plan; otherwise create a new arc (use \`npx @ksoftm/create-arc new "<short title>"\`, or create the file from .arc/_TEMPLATE.md and register it in INDEX.md). Record the raw instruction verbatim, draft the Plan with checkable acceptance criteria, and list the Tasks. Do not start coding until the plan is acknowledged.`,
|
|
339
|
+
},
|
|
340
|
+
"arc-refine": {
|
|
341
|
+
desc: "ARC: fold a new instruction into an existing arc (Refine)",
|
|
342
|
+
body: `Read ./ARC.md, then refine the relevant arc with this instruction:
|
|
343
|
+
|
|
344
|
+
"$ARGUMENTS"
|
|
345
|
+
|
|
346
|
+
Append it verbatim as the next Raw Instruction, add a Refinement Log entry (new plan_version, what changed, task impact), rewrite the Plan to reflect only the current intent, and adjust the Tasks. Move dropped scope to "Out of scope". Never edit the append-only sections retroactively.`,
|
|
347
|
+
},
|
|
348
|
+
"arc-build": {
|
|
349
|
+
desc: "ARC: do the work for the active arc (Read Before / Update After Editing)",
|
|
350
|
+
body: `Read ./ARC.md. Before editing: read .arc/INDEX.md, fully read the arc(s) covering the files you'll touch, and read the real source files. Work the task list in order, marking one task in progress. After editing: advance task states, append a Worklog entry (tasks, files read, files changed, summary, decisions, follow-ups), update the arc frontmatter and its INDEX row, and reference the arc id in the commit. An edit without a worklog entry is unfinished.
|
|
351
|
+
|
|
352
|
+
Focus: $ARGUMENTS`,
|
|
353
|
+
},
|
|
354
|
+
"arc-status": {
|
|
355
|
+
desc: "ARC: summarize all arcs and what to resume",
|
|
356
|
+
body: `Run \`npx @ksoftm/create-arc status\` (or read .arc/INDEX.md and each arc). Report every arc's id, status, plan version, task progress, and which in-progress/refining arcs to resume — reading each one's Status Notes and last Worklog entry.`,
|
|
357
|
+
},
|
|
358
|
+
"arc-resume": {
|
|
359
|
+
desc: "ARC: pick up the in-progress arc cold",
|
|
360
|
+
body: `Read ./ARC.md. Read .arc/INDEX.md, find arcs in in-progress or refining, open them, read Status Notes and the last Worklog entry, then continue from the open tasks — after running Read Before Editing. If code and the arc disagree, the code is truth: note the drift in the Worklog and correct the arc.`,
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// Where each agent looks for project-level slash commands, and how to render one.
|
|
365
|
+
const AGENT_TARGETS = {
|
|
366
|
+
claude: { dir: ".claude/commands", ext: ".md", fmt: md },
|
|
367
|
+
opencode: { dir: ".opencode/command", ext: ".md", fmt: md },
|
|
368
|
+
cursor: { dir: ".cursor/commands", ext: ".md", fmt: md },
|
|
369
|
+
codex: { dir: ".codex/prompts", ext: ".md", fmt: plain }, // Codex custom prompts
|
|
370
|
+
gemini: { dir: ".gemini/commands", ext: ".toml", fmt: toml },
|
|
371
|
+
};
|
|
372
|
+
const ALL_AGENTS = Object.keys(AGENT_TARGETS);
|
|
373
|
+
|
|
374
|
+
function md(name, c) { return `---\ndescription: ${c.desc}\n---\n\n${c.body}\n`; }
|
|
375
|
+
function plain(name, c) { return `${c.body}\n`; }
|
|
376
|
+
function toml(name, c) {
|
|
377
|
+
const b = c.body.replace(/\\/g, "\\\\").replace(/"""/g, '\\"\\"\\"');
|
|
378
|
+
return `description = ${JSON.stringify(c.desc)}\nprompt = """\n${b}\n"""\n`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function cmdAgentInit(flags) {
|
|
382
|
+
const dir = resolve(flags.dir ?? ".");
|
|
383
|
+
// --agents=claude,opencode (default: all)
|
|
384
|
+
const agents = (flags.agents ? String(flags.agents).split(",") : ALL_AGENTS)
|
|
385
|
+
.map((a) => a.trim().toLowerCase()).filter(Boolean);
|
|
386
|
+
const unknown = agents.filter((a) => !AGENT_TARGETS[a]);
|
|
387
|
+
if (unknown.length) return fail(`unknown agent(s): ${unknown.join(", ")} (known: ${ALL_AGENTS.join(", ")})`);
|
|
388
|
+
|
|
389
|
+
let created = 0, skipped = 0;
|
|
390
|
+
for (const agent of agents) {
|
|
391
|
+
const { dir: rel, ext, fmt } = AGENT_TARGETS[agent];
|
|
392
|
+
const outDir = join(dir, rel);
|
|
393
|
+
mkdirSync(outDir, { recursive: true });
|
|
394
|
+
for (const [name, c] of Object.entries(ARC_COMMANDS)) {
|
|
395
|
+
const dest = join(outDir, name + ext);
|
|
396
|
+
if (existsSync(dest) && !flags.force) { skipped++; continue; }
|
|
397
|
+
writeFileSync(dest, fmt(name, c));
|
|
398
|
+
created++;
|
|
399
|
+
}
|
|
400
|
+
console.log(` ${agent.padEnd(9)} ${rel}/ (${Object.keys(ARC_COMMANDS).length} commands)`);
|
|
401
|
+
}
|
|
402
|
+
console.log(`\nAgent commands written (created ${created}, skipped ${skipped}${flags.force ? "" : "; use --force to overwrite"}).`);
|
|
403
|
+
console.log(`Type /arc-new, /arc-build, /arc-status, /arc-refine, /arc-resume in your agent.`);
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
300
407
|
function help() {
|
|
301
408
|
console.log(`create-arc v${PKG.version} — ARC plan-driven development (Align → Refine → Construct)
|
|
409
|
+
(alias: \`arc\`)
|
|
302
410
|
|
|
303
411
|
Usage:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
412
|
+
arc init [dir] [--owner=NAME] scaffold ARC.md + .arc/ (idempotent)
|
|
413
|
+
arc new "Title" [--dir=.] [--tags=a,b] create + register the next arc
|
|
414
|
+
arc status [dir] [--json] status table across all arcs
|
|
415
|
+
arc doctor [dir] consistency checks (exit 1 on problems)
|
|
416
|
+
arc agent-init [--agents=a,b] [--force] write /slash commands for AI agents
|
|
417
|
+
(agents: ${ALL_AGENTS.join(", ")}; default: all)
|
|
418
|
+
|
|
419
|
+
Run with npx (no install): npx @ksoftm/create-arc <command>
|
|
420
|
+
Install globally: npm i -g @ksoftm/create-arc then: arc <command>
|
|
421
|
+
Project dev dependency: npm i -D @ksoftm/create-arc then: npx arc <command>
|
|
309
422
|
|
|
310
423
|
Protocol reference: ARC.md in your project root after init.`);
|
|
311
424
|
return 0;
|
|
@@ -321,6 +434,7 @@ else if (cmd === "init") code = cmdInit(pos[1], flags);
|
|
|
321
434
|
else if (cmd === "new") code = cmdNew(pos.slice(1).join(" "), flags);
|
|
322
435
|
else if (cmd === "status") code = cmdStatus(pos[1], flags);
|
|
323
436
|
else if (cmd === "doctor") code = cmdDoctor(pos[1]);
|
|
324
|
-
else
|
|
437
|
+
else if (cmd === "agent-init" || cmd === "agents") code = cmdAgentInit(flags);
|
|
438
|
+
else code = fail(`unknown command '${cmd}' — try: arc help`);
|
|
325
439
|
|
|
326
440
|
process.exit(code);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ksoftm/create-arc",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scaffold and manage ARC — plan-driven development for AI agents (Align → Refine → Construct). Pure-Markdown plans, tasks, worklogs and statuses in .arc/, for any language and any agent.",
|
|
6
6
|
"keywords": [
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"author": "Ksoftm (Kajalan)",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"bin": {
|
|
22
|
-
"create-arc": "bin/cli.js"
|
|
22
|
+
"create-arc": "bin/cli.js",
|
|
23
|
+
"arc": "bin/cli.js"
|
|
23
24
|
},
|
|
24
25
|
"files": [
|
|
25
26
|
"bin",
|