@vsem/ai 0.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 ADDED
@@ -0,0 +1,28 @@
1
+ # @vsem/ai
2
+
3
+ Install or sync a vsem-framework workspace: creates `.ai/` (rules, roles, tasks,
4
+ decisions, incidents, index), `CLAUDE.md`, `code/`, `materials/`, and sets up git
5
+ with your personal branch.
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ cd my-project
11
+ npx @vsem/ai@latest
12
+ ```
13
+
14
+ Runs against the current directory by default. Optional arguments:
15
+
16
+ ```bash
17
+ npx @vsem/ai@latest [target-dir] [--branch <name>] [--force]
18
+ ```
19
+
20
+ - `target-dir` — install into this directory instead of the current one (created if missing).
21
+ - `--branch <name>` — personal branch name (default: your `git config --global user.name`, or `dev`).
22
+ - `--force` — also overwrite project-owned files (`.ai/project.md`, `.ai/plan.md`, task/decision/incident indexes), resetting them to placeholders. Without this flag, reruns only update framework-owned files (`.ai/rules/`, `.ai/roles/`, `CLAUDE.md`) and never touch your own project state.
23
+
24
+ ## Updating an existing workspace
25
+
26
+ Rerun `npx @vsem/ai@latest` (without `--force`) inside an already-bootstrapped
27
+ workspace to pick up new roles or rule changes published in later versions — your
28
+ `project.md`, `plan.md`, and tasks are left untouched.
package/bin/cli.js ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Install or sync a vsem-framework workspace.
5
+ //
6
+ // This script contains NO framework content (no role text, no rule text). It only
7
+ // knows HOW to install the structure: it copies two template trees from
8
+ // ../templates next to this script into the target workspace, substituting
9
+ // {{VAR}} placeholders, and sets up git.
10
+ //
11
+ // - templates/system/ -> framework-owned files (CLAUDE.md, .ai/rules/*, .ai/roles/*).
12
+ // Always re-copied (overwritten) on every run. To add a new role, drop a file under
13
+ // templates/system/.ai/roles/, bump the package version and publish - no code
14
+ // change needed here.
15
+ // - templates/scaffold/ -> project-owned starting points (.ai/project.md, .ai/plan.md,
16
+ // .ai/tasks/_index.md, etc). Only written if missing, so a rerun never clobbers the
17
+ // user's own project state. Pass --force to overwrite scaffold files too.
18
+ //
19
+ // Usage:
20
+ // npx @vsem/ai@latest [target-dir] [--branch <name>] [--force]
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { execFileSync } = require('child_process');
25
+
26
+ const FRAMEWORK_VERSION = '0.1.0';
27
+ const PACKAGE_DIR = path.join(__dirname, '..');
28
+ const TEMPLATES_DIR = path.join(PACKAGE_DIR, 'templates');
29
+ const SYSTEM_DIR = path.join(TEMPLATES_DIR, 'system');
30
+ const SCAFFOLD_DIR = path.join(TEMPLATES_DIR, 'scaffold');
31
+
32
+ function parseArgs(argv) {
33
+ const opts = { target: null, branch: null, force: false };
34
+ for (let i = 0; i < argv.length; i++) {
35
+ const a = argv[i];
36
+ if (a === '--force') {
37
+ opts.force = true;
38
+ } else if (a === '--branch') {
39
+ opts.branch = argv[++i] || null;
40
+ } else if (a.startsWith('--branch=')) {
41
+ opts.branch = a.slice('--branch='.length);
42
+ } else if (!a.startsWith('-') && opts.target === null) {
43
+ opts.target = a;
44
+ }
45
+ }
46
+ return opts;
47
+ }
48
+
49
+ function walkFiles(dir) {
50
+ const results = [];
51
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
52
+ const full = path.join(dir, entry.name);
53
+ if (entry.isDirectory()) {
54
+ results.push(...walkFiles(full));
55
+ } else if (entry.isFile()) {
56
+ results.push(full);
57
+ }
58
+ }
59
+ return results;
60
+ }
61
+
62
+ // Copies every file under srcRoot into destRoot, preserving relative paths.
63
+ // Strips a trailing ".tmpl" from destination file names. Replaces {{KEY}}
64
+ // tokens. Only overwrites existing destination files when overwrite is true;
65
+ // otherwise leaves them untouched (used for scaffold/ files).
66
+ function copyTemplateTree(srcRoot, destRoot, vars, overwrite) {
67
+ if (!fs.existsSync(srcRoot)) return;
68
+ for (const file of walkFiles(srcRoot)) {
69
+ let rel = path.relative(srcRoot, file);
70
+ if (rel.endsWith('.tmpl')) {
71
+ rel = rel.slice(0, -'.tmpl'.length);
72
+ }
73
+ const dest = path.join(destRoot, rel);
74
+
75
+ if (fs.existsSync(dest) && !overwrite) {
76
+ continue;
77
+ }
78
+
79
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
80
+ let content = fs.readFileSync(file, 'utf8');
81
+ for (const key of Object.keys(vars)) {
82
+ content = content.split(`{{${key}}}`).join(vars[key]);
83
+ }
84
+ fs.writeFileSync(dest, content, 'utf8');
85
+ }
86
+ }
87
+
88
+ function ensureEmptyDirPlaceholder(dir) {
89
+ fs.mkdirSync(dir, { recursive: true });
90
+ if (fs.readdirSync(dir).length === 0) {
91
+ fs.writeFileSync(path.join(dir, '.gitkeep'), '');
92
+ }
93
+ }
94
+
95
+ function tryGit(args, cwd) {
96
+ try {
97
+ return { ok: true, out: execFileSync('git', args, { cwd, encoding: 'utf8' }) };
98
+ } catch (err) {
99
+ return { ok: false, out: '', err };
100
+ }
101
+ }
102
+
103
+ function main() {
104
+ if (!fs.existsSync(SYSTEM_DIR)) {
105
+ console.error(`Templates not found at ${SYSTEM_DIR}. This script must ship together with templates/.`);
106
+ process.exit(1);
107
+ }
108
+
109
+ const args = parseArgs(process.argv.slice(2));
110
+ const target = path.resolve(args.target || process.cwd());
111
+
112
+ fs.mkdirSync(target, { recursive: true });
113
+ const aiDir = path.join(target, '.ai');
114
+ const workspaceName = path.basename(target);
115
+ const isRerun = fs.existsSync(aiDir);
116
+
117
+ let branch = args.branch;
118
+ if (!branch) {
119
+ const cfg = tryGit(['config', '--global', 'user.name'], target);
120
+ if (cfg.ok && cfg.out.trim()) {
121
+ branch = cfg.out.trim().toLowerCase().replace(/\s+/g, '-').replace(/^-+|-+$/g, '');
122
+ }
123
+ if (!branch) branch = 'dev';
124
+ }
125
+
126
+ const vars = { WORKSPACE_NAME: workspaceName, FRAMEWORK_VERSION: FRAMEWORK_VERSION };
127
+
128
+ copyTemplateTree(SYSTEM_DIR, target, vars, true);
129
+ copyTemplateTree(SCAFFOLD_DIR, target, vars, args.force);
130
+
131
+ for (const rel of ['code', 'materials', path.join('.ai', 'memory')]) {
132
+ ensureEmptyDirPlaceholder(path.join(target, rel));
133
+ }
134
+
135
+ if (isRerun) {
136
+ console.log(`[ok] Workspace synced at ${target} (system files updated; scaffold files kept unless --force)`);
137
+ } else {
138
+ console.log(`[ok] Workspace structure created at ${target}`);
139
+ }
140
+
141
+ const gitCheck = tryGit(['--version'], target);
142
+ if (!gitCheck.ok) {
143
+ console.log('[warn] git not found in PATH - skipping git init.');
144
+ return;
145
+ }
146
+
147
+ if (!fs.existsSync(path.join(target, '.git'))) {
148
+ tryGit(['init', '-q'], target);
149
+ console.log('[ok] git init');
150
+ }
151
+
152
+ const currentBranchResult = tryGit(['branch', '--show-current'], target);
153
+ const currentBranch = currentBranchResult.ok ? currentBranchResult.out.trim() : '';
154
+
155
+ if (currentBranch !== branch) {
156
+ const branchExists = tryGit(['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], target).ok;
157
+ if (branchExists) {
158
+ tryGit(['checkout', '-q', branch], target);
159
+ } else {
160
+ tryGit(['checkout', '-q', '-b', branch], target);
161
+ }
162
+ console.log(`[ok] branch '${branch}' is active`);
163
+ }
164
+
165
+ tryGit(['add', '-A'], target);
166
+ const noChanges = tryGit(['diff', '--cached', '--quiet'], target).ok;
167
+
168
+ if (!noChanges) {
169
+ const msg = isRerun ? 'chore: sync vsem-framework system files' : 'chore: bootstrap vsem-framework workspace';
170
+ tryGit(['commit', '-q', '-m', msg], target);
171
+ console.log(`[ok] commit created: ${msg}`);
172
+ } else {
173
+ console.log('[skip] nothing to commit (already up to date)');
174
+ }
175
+ }
176
+
177
+ main();
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@vsem/ai",
3
+ "version": "0.1.0",
4
+ "description": "Install or sync a vsem-framework workspace (.ai/ structure, roles, rules) via npx.",
5
+ "bin": {
6
+ "vsem-ai": "bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "templates"
11
+ ],
12
+ "engines": {
13
+ "node": ">=14"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "license": "UNLICENSED",
19
+ "keywords": [
20
+ "ai",
21
+ "framework",
22
+ "scaffolding",
23
+ "claude"
24
+ ]
25
+ }
@@ -0,0 +1,3 @@
1
+ # Decision Log
2
+
3
+ > One entry = one decision. Maintained by ProjectManager.
@@ -0,0 +1,3 @@
1
+ # Incident Log
2
+
3
+ > Failures/errors in role work - separate from the decision log.
@@ -0,0 +1,3 @@
1
+ # Document / Code Graph Index
2
+
3
+ > Mechanism not chosen yet (see the framework's own roadmap). Empty for now.
@@ -0,0 +1,8 @@
1
+ # Plan - {{WORKSPACE_NAME}}
2
+
3
+ > TODO: filled in by the PM role after `.ai/project.md` is approved.
4
+ > Decomposition happens incrementally as the project progresses, not all at
5
+ > once at the start.
6
+
7
+ ## Phase 0
8
+ - [ ] TODO
@@ -0,0 +1,17 @@
1
+ # Project - {{WORKSPACE_NAME}}
2
+
3
+ > TODO: filled in by the PM role after a requirements dialogue with the user
4
+ > (goal, audience, constraints, MVP scope). Source material - see `materials/`.
5
+
6
+ ## Vision
7
+ TODO
8
+
9
+ ## Goals
10
+ TODO
11
+
12
+ ## Non-goals
13
+ TODO
14
+
15
+ ## Key decisions log
16
+ | Date | Decision | Reason |
17
+ |------|----------|--------|
@@ -0,0 +1,8 @@
1
+ # Tasks Index
2
+
3
+ > Auto-generated summary of `.ai/tasks/*.md`, so roles don't need to read every
4
+ > file - saves tokens (see the ContextManager role). Task format - see
5
+ > `.ai/rules/tasks-format.md`.
6
+
7
+ | id | title | owner_role | status | priority |
8
+ |----|-------|------------|--------|----------|
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: architect
3
+ ---
4
+
5
+ # Architect
6
+
7
+ ## Responsible for
8
+ Architectural and technical decisions at project/module level.
9
+
10
+ ## Can block
11
+ Developer (until the decision is reconsidered).
12
+
13
+ ## Escalates to the human when
14
+ The decision is irreversible or affects multiple code repositories at once.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: context-manager
3
+ ---
4
+
5
+ # ContextManager (system role)
6
+
7
+ ## Responsible for
8
+ Assembles the minimally sufficient context for a role+task (token economy).
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ Session token budget is at risk of being exceeded.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: conventions-guardian
3
+ ---
4
+
5
+ # ConventionsGuardian
6
+
7
+ ## Responsible for
8
+ Checks that code/documents comply with framework conventions; guards the hard rule "never change .ai/ structure without confirmation".
9
+
10
+ ## Can block
11
+ Any role, if it violates the .ai/ structure or conventions.
12
+
13
+ ## Escalates to the human when
14
+ A violation of .ai/ structure is found - escalate immediately, without attempting to fix it itself.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: designer
3
+ ---
4
+
5
+ # Designer
6
+
7
+ ## Responsible for
8
+ UX/UI decisions, design mockups.
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ No approved materials to rely on (no spec/references).
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: developer
3
+ ---
4
+
5
+ # Developer
6
+
7
+ ## Responsible for
8
+ Implementation of code in code/* per task.
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ The task is ambiguous and requires an architectural decision that does not exist yet.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: indexer-librarian
3
+ ---
4
+
5
+ # Indexer/Librarian
6
+
7
+ ## Responsible for
8
+ Maintains the document/code index/graph in .ai/index/ (exact mechanism still being designed).
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ Found a contradiction between documents it cannot resolve on its own.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: project-manager
3
+ ---
4
+
5
+ # ProjectManager (PM)
6
+
7
+ ## Responsible for
8
+ Task decomposition and prioritization (incrementally, as the project progresses), final decision in role conflicts, maintaining the decision log, distributing work across roles.
9
+
10
+ ## Can block
11
+ Can pause any role in case of priority conflicts.
12
+
13
+ ## Escalates to the human when
14
+ Not confident in resolving the conflict itself; the question affects MVP scope/deadlines.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: reviewer-qa
3
+ ---
4
+
5
+ # Reviewer/QA
6
+
7
+ ## Responsible for
8
+ Quality check of task results before closing (tests, linter, review).
9
+
10
+ ## Can block
11
+ Developer (task stays open until the gate is passed).
12
+
13
+ ## Escalates to the human when
14
+ The found issue is systemic (not scoped to a single task).
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: technical-writer
3
+ ---
4
+
5
+ # TechnicalWriter
6
+
7
+ ## Responsible for
8
+ Documentation for humans (README, guides) outside .ai/ - inside .ai/ the structure itself is the documentation.
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ Not enough information from other roles to describe the feature.
@@ -0,0 +1,14 @@
1
+ ---
2
+ role_id: tester
3
+ ---
4
+
5
+ # Tester
6
+
7
+ ## Responsible for
8
+ Test scenarios and running them (may overlap with Reviewer/QA - the split is refined as the project evolves).
9
+
10
+ ## Can block
11
+ -
12
+
13
+ ## Escalates to the human when
14
+ Cannot reproduce/confirm the bug.
@@ -0,0 +1,28 @@
1
+ ---
2
+ framework_version: "{{FRAMEWORK_VERSION}}"
3
+ ---
4
+
5
+ # Core Rules (vsem-framework)
6
+
7
+ 1. **Never change the structure of `.ai/`** (create/delete/rename top-level
8
+ folders inside it) without explicit human confirmation. This is the main rule
9
+ for every role/agent working in this workspace.
10
+ 2. Roles work within a limited, role-scoped context - do not read the whole
11
+ repository unless the task requires it. Context for a task is assembled by
12
+ ContextManager.
13
+ 3. Conflicts between roles are resolved by ProjectManager; the decision is
14
+ recorded in `.ai/decisions/`. If PM itself is not sure, escalate to the human.
15
+ 4. Autonomy by default - the framework acts on its own. Stop and ask the human
16
+ when:
17
+ - about to merge the personal branch into `main`;
18
+ - the operation deletes data or is irreversible;
19
+ - an external access/secret/integration is required;
20
+ - the decision is architectural at the whole-project level;
21
+ - the role itself is not sure about the decision.
22
+ 5. In autonomous mode, commits go to the user's personal branch, never to `main`.
23
+ 6. `code/*` is code only (git submodules). `materials/` is raw input from the
24
+ user. `.ai/` holds the entire methodology and working state of the project.
25
+ 7. A task is not closed (`status: done`) without passing its quality gate, if
26
+ one is required for it (see `.ai/roles/reviewer-qa.md`).
27
+
28
+ See also `.ai/project.md` (this project's description) and `.ai/plan.md` (this project's plan).
@@ -0,0 +1,36 @@
1
+ # Task Format
2
+
3
+ One file = one task, in `.ai/tasks/`. File name: `<id>-<short-slug>.md`
4
+ (id = zero-padded sequence number, e.g. `0001`).
5
+
6
+ ## Frontmatter
7
+
8
+ ```yaml
9
+ ---
10
+ id: "0001"
11
+ title: "Short task title"
12
+ owner_role: developer # one of the roles in .ai/roles/, lowercase
13
+ status: todo # todo | in_progress | blocked-on-human | blocked-on-dependency | blocked-on-external | in_review | done
14
+ priority: must # must | nice
15
+ depends_on: [] # ids of blocking tasks, e.g. ["0002"]
16
+ linked_files: # paths to files/modules this task touches
17
+ - code/backend/src/auth/
18
+ created: YYYY-MM-DD
19
+ updated: YYYY-MM-DD
20
+ ---
21
+ ```
22
+
23
+ ## Body
24
+ - **Description** - what needs to be done and why.
25
+ - **Acceptance criteria** - short checklist Reviewer/QA uses to confirm the task is done.
26
+ - **Notes** - optional work log.
27
+
28
+ ## Status transitions
29
+ `todo -> in_progress -> in_review -> done`
30
+ Side branches: `blocked-on-*` (from any active status, back to `in_progress` once unblocked).
31
+
32
+ ## Who sets what
33
+ - `priority` - ProjectManager.
34
+ - `owner_role` - ProjectManager, during decomposition.
35
+ - `status` - the owning role, as work progresses; moving to `done` requires
36
+ passing the quality gate (Reviewer/QA) if one applies to the task.
@@ -0,0 +1,8 @@
1
+ # {{WORKSPACE_NAME}}
2
+
3
+ This project is managed by vsem-framework.
4
+ Work according to the rules and structure in `.ai/`.
5
+ Start with `.ai/rules/core.md` and `.ai/project.md`.
6
+
7
+ **Hard rule:** never modify the top-level structure of `.ai/` (create/delete/rename
8
+ top-level folders inside it) without explicit human confirmation.