@p_tipso/agentive 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pakawat Tipso
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ <![CDATA[<div align="center">
2
+
3
+ # 🤖 agentive
4
+
5
+ **A universal, framework-agnostic AI agent workspace CLI.**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@p_tipso/agentive.svg?style=flat-square)](https://www.npmjs.com/package/@p_tipso/agentive)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ Stop maintaining separate rule files for every AI tool. **agentive** scaffolds a single `.agent/` directory in your project — your source of truth for agent rules and skills — and compiles it into the specific formats each AI tool understands.
16
+
17
+ ## ✨ What it does
18
+
19
+ 1. **Scaffolds** a universal `.agent/` workspace in your project
20
+ 2. **Syncs** your `.agent/` files into the native formats of your chosen AI tools:
21
+ - 🖱 **Cursor** → `.cursor/rules/`
22
+ - 🤖 **Claude** → `CLAUDE.md`
23
+ - 🌊 **Windsurf** → `.windsurfrules`
24
+
25
+ ---
26
+
27
+ ## 🚀 Quick Start
28
+
29
+ ```bash
30
+ npx @p_tipso/agentive
31
+ ```
32
+
33
+ The interactive wizard will ask you:
34
+ 1. **Project name** (pre-filled from your directory name)
35
+ 2. **Your primary tech stack** (e.g. "Next.js, TypeScript, Postgres")
36
+ 3. **Which AI tools** you use (multi-select: Cursor, Claude, Windsurf)
37
+
38
+ Then it scaffolds everything automatically.
39
+
40
+ ---
41
+
42
+ ## 📁 Output Structure
43
+
44
+ After running `npx @p_tipso/agentive`, your project will have:
45
+
46
+ ```
47
+ your-project/
48
+ ├── .agent/
49
+ │ ├── settings.json ← project config (commit this)
50
+ │ ├── settings.local.json ← local overrides (gitignored automatically)
51
+ │ ├── rules/
52
+ │ │ └── architecture.md ← project-wide rules for all agents
53
+ │ └── skills/
54
+ │ ├── web-browser.md ← how agents should browse the web
55
+ │ ├── data-extractor.md ← how agents should extract data
56
+ │ ├── data-transformer.md ← how agents should transform data
57
+ │ └── output-dispatcher.md ← how agents should deliver output
58
+
59
+ │ ── (auto-synced based on your selection) ──
60
+
61
+ ├── .cursor/rules/ ← if you selected Cursor
62
+ ├── CLAUDE.md ← if you selected Claude
63
+ └── .windsurfrules ← if you selected Windsurf
64
+ ```
65
+
66
+ ### `settings.json` example
67
+
68
+ ```json
69
+ {
70
+ "projectName": "my-app",
71
+ "techStack": "Next.js, TypeScript, PostgreSQL",
72
+ "tools": ["cursor", "claude"],
73
+ "agentiveVersion": "1.0.0",
74
+ "createdAt": "2026-07-05T12:00:00.000Z"
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 📦 Commands
81
+
82
+ | Command | Description |
83
+ |---|---|
84
+ | `npx @p_tipso/agentive` | Run the setup wizard and scaffold your `.agent/` workspace |
85
+ | `npx @p_tipso/agentive init` | Same as above (explicit subcommand) |
86
+ | `npx @p_tipso/agentive --version` | Print the current version |
87
+ | `npx @p_tipso/agentive --help` | Show available commands |
88
+
89
+ ---
90
+
91
+ ## 🛠 Install Globally (optional)
92
+
93
+ If you use this frequently, install it globally:
94
+
95
+ ```bash
96
+ npm install -g @p_tipso/agentive
97
+ agentive
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 🔄 Re-syncing
103
+
104
+ Edited your `.agent/` files and want to push changes to Cursor, Claude, etc.? Just re-run:
105
+
106
+ ```bash
107
+ npx @p_tipso/agentive
108
+ ```
109
+
110
+ It will detect the existing `.agent/` directory and ask before overwriting.
111
+
112
+ ---
113
+
114
+ ## 🤝 Contributing
115
+
116
+ 1. Fork the repo: [github.com/TiPS0/agentive](https://github.com/TiPS0/agentive)
117
+ 2. Clone your fork
118
+ 3. `npm install`
119
+ 4. `npm link` (to use `agentive` locally while developing)
120
+ 5. Make your changes and open a pull request!
121
+
122
+ ---
123
+
124
+ ## 📄 License
125
+
126
+ MIT © [Pakawat Tipso](https://github.com/TiPS0)
127
+ ]]>
package/bin/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { Command } = require('commander');
6
+ const { version } = require('../package.json');
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name('agentive')
12
+ .description('Universal AI agent workspace setup CLI')
13
+ .version(version, '-v, --version', 'Print the current version');
14
+
15
+ program
16
+ .command('init', { isDefault: true })
17
+ .description('Initialize a universal .agent/ workspace in your project')
18
+ .action(async () => {
19
+ const { runInit } = require('../src/commands/init');
20
+ await runInit();
21
+ });
22
+
23
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@p_tipso/agentive",
3
+ "version": "1.0.0",
4
+ "description": "A universal, framework-agnostic AI agent repository setup CLI. Scaffold a .agent/ workspace and sync to Cursor, Claude, and Windsurf.",
5
+ "license": "MIT",
6
+ "author": "Pakawat Tipso",
7
+ "type": "commonjs",
8
+ "engines": {
9
+ "node": ">=18"
10
+ },
11
+ "bin": {
12
+ "agentive": "bin/index.js"
13
+ },
14
+ "files": [
15
+ "bin/",
16
+ "src/",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "ai",
22
+ "agent",
23
+ "cli",
24
+ "cursor",
25
+ "claude",
26
+ "windsurf",
27
+ "scaffold",
28
+ "init",
29
+ "rules",
30
+ "context"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/TiPS0/agentive.git"
35
+ },
36
+ "homepage": "https://github.com/TiPS0/agentive#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/TiPS0/agentive/issues"
39
+ },
40
+ "scripts": {
41
+ "prepare": "node -e \"console.log('agentive ready.')\"",
42
+ "test": "node bin/index.js --version"
43
+ },
44
+ "dependencies": {
45
+ "chalk": "^4.1.2",
46
+ "commander": "^12.1.0",
47
+ "prompts": "^2.4.2"
48
+ }
49
+ }
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const prompts = require('prompts');
5
+ const chalk = require('chalk');
6
+ const { version } = require('../../package.json');
7
+ const {
8
+ createAgentDirectory,
9
+ copyTemplates,
10
+ agentDirectoryExists,
11
+ writeSettings,
12
+ } = require('../utils/fileSystem');
13
+ const { syncToCursor, syncToClaude, syncToWindsurf } = require('../utils/compilers');
14
+
15
+ const AI_TOOLS = [
16
+ { title: '🖱 Cursor', value: 'cursor', description: 'Generates .cursor/rules/' },
17
+ { title: '🤖 Claude', value: 'claude', description: 'Generates CLAUDE.md' },
18
+ { title: '🌊 Windsurf', value: 'windsurf', description: 'Generates .windsurfrules' },
19
+ ];
20
+
21
+ async function runInit() {
22
+ const cwd = process.cwd();
23
+
24
+ console.log('');
25
+ console.log(chalk.bold.cyan(' 🤖 agentive') + chalk.gray(' — Universal AI Agent Workspace Setup'));
26
+ console.log(chalk.gray(' ─────────────────────────────────────────────'));
27
+ console.log('');
28
+
29
+ // --- Guard: check if .agent/ already exists ---
30
+ const alreadyExists = await agentDirectoryExists(cwd);
31
+ if (alreadyExists) {
32
+ const { overwrite } = await prompts({
33
+ type: 'confirm',
34
+ name: 'overwrite',
35
+ message: chalk.yellow('A .agent/ directory already exists. Overwrite it?'),
36
+ initial: false,
37
+ });
38
+
39
+ if (!overwrite) {
40
+ console.log('');
41
+ console.log(chalk.gray(' Aborted. Your existing .agent/ directory was not modified.'));
42
+ console.log('');
43
+ process.exit(0);
44
+ }
45
+ }
46
+
47
+ // --- Wizard ---
48
+ const projectName = path.basename(cwd);
49
+
50
+ const answers = await prompts(
51
+ [
52
+ {
53
+ type: 'text',
54
+ name: 'projectName',
55
+ message: 'Project name?',
56
+ initial: projectName,
57
+ validate: (v) => v.trim().length > 0 || 'Required.',
58
+ },
59
+ {
60
+ type: 'text',
61
+ name: 'techStack',
62
+ message: 'Primary tech stack?',
63
+ placeholder: 'e.g. Next.js, TypeScript, PostgreSQL',
64
+ validate: (v) => v.trim().length > 0 || 'Required.',
65
+ },
66
+ {
67
+ type: 'multiselect',
68
+ name: 'tools',
69
+ message: 'Which AI tools do you want to target?',
70
+ choices: AI_TOOLS,
71
+ min: 1,
72
+ hint: '- Space to select. Return to submit',
73
+ instructions: false,
74
+ },
75
+ ],
76
+ {
77
+ onCancel: () => {
78
+ console.log('');
79
+ console.log(chalk.gray(' Setup cancelled.'));
80
+ process.exit(0);
81
+ },
82
+ }
83
+ );
84
+
85
+ const { tools, techStack } = answers;
86
+ const finalProjectName = answers.projectName;
87
+
88
+ console.log('');
89
+ console.log(chalk.gray(' Setting up your workspace...'));
90
+ console.log('');
91
+
92
+ // --- Scaffold .agent/ directory ---
93
+ const templatesDir = path.join(__dirname, '..', 'templates');
94
+ const agentDir = await createAgentDirectory(cwd, alreadyExists);
95
+ await copyTemplates(templatesDir, agentDir, techStack);
96
+
97
+ // --- Write settings.json and settings.local.json ---
98
+ await writeSettings(agentDir, {
99
+ projectName: finalProjectName,
100
+ techStack,
101
+ tools,
102
+ agentiveVersion: version,
103
+ createdAt: new Date().toISOString(),
104
+ });
105
+
106
+ console.log(chalk.green(' ✔ ') + chalk.white('Scaffolded ') + chalk.cyan('.agent/') + chalk.white(' workspace'));
107
+
108
+ // --- Sync to selected tools ---
109
+ const compilers = {
110
+ cursor: syncToCursor,
111
+ claude: syncToClaude,
112
+ windsurf: syncToWindsurf,
113
+ };
114
+
115
+ const toolLabels = {
116
+ cursor: '.cursor/rules/',
117
+ claude: 'CLAUDE.md',
118
+ windsurf: '.windsurfrules',
119
+ };
120
+
121
+ for (const tool of tools) {
122
+ try {
123
+ await compilers[tool](agentDir, cwd, techStack);
124
+ console.log(chalk.green(' ✔ ') + chalk.white('Synced → ') + chalk.cyan(toolLabels[tool]));
125
+ } catch (err) {
126
+ console.log(chalk.red(' ✘ ') + chalk.white(`Failed to sync ${tool}: `) + chalk.gray(err.message));
127
+ }
128
+ }
129
+
130
+ // --- Done ---
131
+ console.log('');
132
+ console.log(chalk.bold.green(' ✅ All done!'));
133
+ console.log('');
134
+ console.log(chalk.white(' Your AI workspace is ready at ') + chalk.cyan('.agent/'));
135
+ console.log('');
136
+ console.log(chalk.gray(' Structure:'));
137
+ console.log(chalk.gray(' .agent/'));
138
+ console.log(chalk.gray(' ├── settings.json ← project config (commit this)'));
139
+ console.log(chalk.gray(' ├── settings.local.json ← local overrides (gitignored)'));
140
+ console.log(chalk.gray(' ├── rules/ ← agent rules'));
141
+ console.log(chalk.gray(' └── skills/ ← agent skills'));
142
+ console.log('');
143
+ console.log(chalk.gray(' Edit the markdown files in .agent/ to customise agent behaviour.'));
144
+ console.log(chalk.gray(' Re-run ') + chalk.cyan('npx @p_tipso/agentive') + chalk.gray(' any time to re-sync to your AI tools.'));
145
+ console.log('');
146
+ }
147
+
148
+ module.exports = { runInit };
@@ -0,0 +1,62 @@
1
+ # Project Overview
2
+
3
+ > This file provides essential context for AI agents working on this project.
4
+ > Keep it updated as the project evolves.
5
+
6
+ ## About This Project
7
+
8
+ <!-- Describe what this project does and who it is for -->
9
+
10
+ **Tech Stack:** {{TECH_STACK}}
11
+
12
+ ## Project Goals
13
+
14
+ <!-- List the primary goals and success criteria for this project -->
15
+
16
+ 1.
17
+ 2.
18
+ 3.
19
+
20
+ ## Architecture Overview
21
+
22
+ <!-- Briefly describe the high-level architecture and major components -->
23
+
24
+ ```
25
+ your-project/
26
+ ├── src/ # Application source code
27
+ ├── tests/ # Test files
28
+ └── docs/ # Documentation
29
+ ```
30
+
31
+ ## Key Conventions
32
+
33
+ <!-- Document conventions that are unique to this project -->
34
+
35
+ - **Branch naming**: `feat/`, `fix/`, `chore/` prefixes
36
+ - **Commit messages**: Follow [Conventional Commits](https://www.conventionalcommits.org/)
37
+ - **PR size**: Keep pull requests focused and under 400 lines changed
38
+
39
+ ## Important Files & Directories
40
+
41
+ | Path | Purpose |
42
+ |------|---------|
43
+ | `src/` | Application source |
44
+ | `.ai/` | Agent rules, skills, and context (source of truth) |
45
+
46
+ ## External Services & APIs
47
+
48
+ <!-- List any external services, APIs, or integrations this project depends on -->
49
+
50
+ | Service | Purpose | Docs |
51
+ |---------|---------|------|
52
+ | | | |
53
+
54
+ ## Known Gotchas
55
+
56
+ <!-- Document non-obvious things that have caused bugs or confusion in the past -->
57
+
58
+ -
59
+
60
+ ---
61
+
62
+ *Generated by [agentive](https://github.com/TiPS0/agentive) on {{YEAR}}.*
@@ -0,0 +1,42 @@
1
+ # Architecture Rules
2
+
3
+ > These rules govern how AI agents should reason about, write, and modify code
4
+ > in this project. All agents operating in this repository must follow them.
5
+
6
+ ## Tech Stack
7
+
8
+ This project uses: **{{TECH_STACK}}**
9
+
10
+ All generated code must be idiomatic for this stack and follow its established patterns.
11
+
12
+ ## Structural Principles
13
+
14
+ - **Single Responsibility**: Every function, module, or class should do one thing well.
15
+ - **Separation of Concerns**: Keep data fetching, business logic, and presentation in separate layers.
16
+ - **DRY (Don't Repeat Yourself)**: Before adding new code, search for existing utilities or helpers.
17
+ - **YAGNI (You Aren't Gonna Need It)**: Do not add features or abstractions that aren't required right now.
18
+
19
+ ## Code Style
20
+
21
+ - Prefer **async/await** over raw Promises or callbacks.
22
+ - Always handle errors explicitly — never swallow exceptions silently.
23
+ - Use **descriptive, intent-revealing names** for variables, functions, and files.
24
+ - Write **small, focused functions** (prefer < 30 lines per function).
25
+ - Add **JSDoc or TSDoc comments** to all exported functions.
26
+
27
+ ## File Organisation
28
+
29
+ - Group related code by **feature**, not by type (avoid giant `utils/` or `helpers/` dumping grounds).
30
+ - Keep files focused — if a file grows past ~200 lines, consider splitting it.
31
+ - Barrel exports (`index.js`) should only re-export, never contain logic.
32
+
33
+ ## Testing
34
+
35
+ - Every new feature should have a corresponding test.
36
+ - Test **behaviour**, not implementation details.
37
+ - Prefer unit tests for pure functions; integration tests for side-effecting code.
38
+
39
+ ## Dependencies
40
+
41
+ - Before adding a new dependency, consider if the task can be accomplished with built-ins.
42
+ - Avoid dependencies with no active maintenance or that have known security advisories.
@@ -0,0 +1,56 @@
1
+ # Skill: Data Extractor Agent
2
+
3
+ > This skill defines how an AI agent should behave when its role is to
4
+ > extract structured data from raw, unstructured, or semi-structured sources.
5
+
6
+ ## Role Description
7
+
8
+ You are a **Data Extractor Agent**. Your job is to parse raw content (HTML, text,
9
+ JSON, PDF, CSV, etc.) and extract specific, structured data points as defined by
10
+ the schema provided by the orchestrator. You surface data — you do not transform
11
+ or enrich it.
12
+
13
+ ## Responsibilities
14
+
15
+ - Parse the provided raw content using the most appropriate method for its format.
16
+ - Extract only the fields specified in the schema — do not add extra fields.
17
+ - Return `null` for any field that cannot be found; do not guess or hallucinate values.
18
+ - Normalise whitespace and remove HTML tags from text values.
19
+ - Flag ambiguous or low-confidence extractions to the orchestrator.
20
+
21
+ ## Inputs
22
+
23
+ | Input | Type | Description |
24
+ |-------|------|-------------|
25
+ | `content` | `string` | The raw content to parse |
26
+ | `format` | `string` | One of: `html`, `text`, `json`, `csv`, `markdown` |
27
+ | `schema` | `object` | JSON Schema defining the fields to extract |
28
+
29
+ ## Outputs
30
+
31
+ ```json
32
+ {
33
+ "data": {
34
+ "field_name": "extracted value",
35
+ "another_field": null
36
+ },
37
+ "confidence": 0.95,
38
+ "warnings": ["Could not find 'phone_number' in source"]
39
+ }
40
+ ```
41
+
42
+ ## Constraints
43
+
44
+ - **Never infer** or fabricate a value that isn't explicitly present in the source.
45
+ - **Always** include a `confidence` score between `0.0` and `1.0`.
46
+ - If `confidence < 0.7`, always include a `warnings` entry explaining the uncertainty.
47
+ - **Do not** make external network requests — work only with the content provided.
48
+
49
+ ## Error Handling
50
+
51
+ | Error | Response |
52
+ |-------|----------|
53
+ | `Unsupported format` | Return an error; list supported formats |
54
+ | `Schema mismatch` | Return partial data with warnings for missing fields |
55
+ | `Empty content` | Return all fields as `null` with a warning |
56
+ | `Parse error` | Report the specific parse error and return an empty `data` object |
@@ -0,0 +1,62 @@
1
+ # Skill: Data Transformer Agent
2
+
3
+ > This skill defines how an AI agent should behave when its role is to
4
+ > clean, enrich, reformat, or restructure data from one schema to another.
5
+
6
+ ## Role Description
7
+
8
+ You are a **Data Transformer Agent**. Your job is to take structured data
9
+ produced by an extractor or another upstream agent and apply a set of
10
+ transformation rules to produce a new, target schema. You bridge raw extracted
11
+ data and the format needed by the output dispatcher or downstream system.
12
+
13
+ ## Responsibilities
14
+
15
+ - Apply the transformation rules defined in the task to reshape input data.
16
+ - Normalise dates to ISO 8601 format (`YYYY-MM-DDTHH:mm:ssZ`) unless told otherwise.
17
+ - Normalise currency values to a base unit (e.g. cents) when dealing with financial data.
18
+ - Deduplicate records when a `dedup_key` is specified.
19
+ - Apply computed fields (e.g. `full_name = first_name + " " + last_name`).
20
+ - Report rows/records that could not be transformed and explain why.
21
+
22
+ ## Inputs
23
+
24
+ | Input | Type | Description |
25
+ |-------|------|-------------|
26
+ | `data` | `object \| array` | The raw structured data to transform |
27
+ | `rules` | `object` | Transformation rules (field mappings, computed fields, filters) |
28
+ | `target_schema` | `object` | The JSON Schema of the expected output |
29
+
30
+ ## Outputs
31
+
32
+ ```json
33
+ {
34
+ "transformed": [
35
+ { "id": "abc-123", "full_name": "Ada Lovelace", "created_at": "1815-12-10T00:00:00Z" }
36
+ ],
37
+ "failed": [
38
+ { "record": { "raw": "..." }, "reason": "Missing required field: email" }
39
+ ],
40
+ "stats": {
41
+ "input_count": 10,
42
+ "success_count": 9,
43
+ "failed_count": 1
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Constraints
49
+
50
+ - **Do not** drop records silently — every failed transformation must appear in `failed`.
51
+ - **Do not** make network requests to enrich data — use only what was provided.
52
+ - **Preserve** the original data types unless an explicit cast is in the rules.
53
+ - When a rule is ambiguous, apply the safest interpretation and add a warning.
54
+
55
+ ## Error Handling
56
+
57
+ | Error | Response |
58
+ |-------|----------|
59
+ | `Invalid rule` | Report the specific rule that is invalid; skip that rule; continue with others |
60
+ | `Schema violation` | Include the violating record in `failed` with the validation error |
61
+ | `Empty input` | Return `{ transformed: [], failed: [], stats: { ... } }` immediately |
62
+ | `Type mismatch` | Attempt a safe cast; if not possible, add the record to `failed` |
@@ -0,0 +1,79 @@
1
+ # Skill: Output Dispatcher Agent
2
+
3
+ > This skill defines how an AI agent should behave when its role is to
4
+ > deliver final, processed data to one or more target destinations.
5
+
6
+ ## Role Description
7
+
8
+ You are an **Output Dispatcher Agent**. Your job is to take the final,
9
+ transformed data from an upstream agent and reliably deliver it to the
10
+ specified destination(s). You are responsible for formatting, serialisation,
11
+ and confirming successful delivery. You are the last step in the pipeline.
12
+
13
+ ## Responsibilities
14
+
15
+ - Serialise data into the format required by the destination (`json`, `csv`, `markdown`, `plain text`, etc.).
16
+ - Write to file, send to an API endpoint, or save to a database as instructed.
17
+ - Confirm successful delivery and report the destination path or response.
18
+ - Handle partial failures gracefully — report which destinations succeeded and which failed.
19
+ - Never modify the data content — your job is delivery, not transformation.
20
+
21
+ ## Inputs
22
+
23
+ | Input | Type | Description |
24
+ |-------|------|-------------|
25
+ | `data` | `object \| array` | The final data to deliver |
26
+ | `destinations` | `array` | List of destination configs (see below) |
27
+ | `format` | `string` | Output format: `json`, `csv`, `markdown`, `text` |
28
+
29
+ ### Destination Config Shape
30
+
31
+ ```json
32
+ {
33
+ "type": "file",
34
+ "path": "./output/results.json"
35
+ }
36
+ ```
37
+
38
+ ```json
39
+ {
40
+ "type": "api",
41
+ "url": "https://api.example.com/results",
42
+ "method": "POST",
43
+ "headers": { "Authorization": "Bearer <token>" }
44
+ }
45
+ ```
46
+
47
+ ## Outputs
48
+
49
+ ```json
50
+ {
51
+ "results": [
52
+ { "destination": "./output/results.json", "status": "success", "bytes_written": 4096 },
53
+ { "destination": "https://api.example.com/results", "status": "failed", "error": "401 Unauthorized" }
54
+ ],
55
+ "summary": {
56
+ "total": 2,
57
+ "succeeded": 1,
58
+ "failed": 1
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Constraints
64
+
65
+ - **Do not** alter data content during serialisation — only change the representation.
66
+ - **Always** report `status` for every destination, even on failure.
67
+ - **Do not** retry automatically — report the failure and let the orchestrator decide.
68
+ - Respect rate limits when dispatching to APIs — add delays if explicitly instructed.
69
+ - **Never** log or store sensitive values (tokens, passwords) in the output.
70
+
71
+ ## Error Handling
72
+
73
+ | Error | Response |
74
+ |-------|----------|
75
+ | `File write error` | Report the OS error; mark destination as `failed` |
76
+ | `API 4xx` | Report status code and body; mark as `failed`; do not retry |
77
+ | `API 5xx` | Report status code; mark as `failed`; note retry is possible |
78
+ | `Invalid format` | Report the unsupported format; list supported formats |
79
+ | `Network timeout` | Report timeout after 30 seconds; mark as `failed` |
@@ -0,0 +1,54 @@
1
+ # Skill: Web Browser Agent
2
+
3
+ > This skill defines how an AI agent should behave when its role is to
4
+ > browse the web, retrieve information, and interact with web pages.
5
+
6
+ ## Role Description
7
+
8
+ You are a **Web Browser Agent**. Your job is to navigate the internet, retrieve
9
+ accurate information from URLs, and return structured data to the orchestrator.
10
+ You do not transform or interpret the data — you fetch and return it faithfully.
11
+
12
+ ## Responsibilities
13
+
14
+ - Navigate to a given URL and retrieve its full page content.
15
+ - Extract the visible text content, ignoring boilerplate (navbars, footers, ads).
16
+ - Follow links within a domain when explicitly instructed to crawl.
17
+ - Report the final resolved URL (after redirects) alongside the content.
18
+ - Respect `robots.txt` and avoid rate-limiting target servers.
19
+
20
+ ## Inputs
21
+
22
+ | Input | Type | Description |
23
+ |-------|------|-------------|
24
+ | `url` | `string` | The URL to navigate to |
25
+ | `selector` | `string?` | Optional CSS selector to target specific content |
26
+ | `follow_links` | `boolean?` | Whether to follow internal links (default: `false`) |
27
+
28
+ ## Outputs
29
+
30
+ ```json
31
+ {
32
+ "url": "https://example.com/page",
33
+ "title": "Page Title",
34
+ "content": "Extracted text content...",
35
+ "links": ["https://example.com/related"],
36
+ "status": 200
37
+ }
38
+ ```
39
+
40
+ ## Constraints
41
+
42
+ - **Do not execute** scripts or interact with dynamic JavaScript unless using a headless browser tool.
43
+ - **Do not** store credentials or session cookies beyond the current task.
44
+ - **Always** return a `status` code and surface any HTTP errors to the orchestrator.
45
+ - If a page returns a non-200 status, report the error and stop — do not retry without explicit instruction.
46
+
47
+ ## Error Handling
48
+
49
+ | Error | Response |
50
+ |-------|----------|
51
+ | `404 Not Found` | Report to orchestrator; do not guess alternative URLs |
52
+ | `403 Forbidden` | Report to orchestrator; do not attempt bypass |
53
+ | `Timeout` | Retry once after 5 seconds; report failure if still unresponsive |
54
+ | `Invalid URL` | Return an error immediately without making a network request |
@@ -0,0 +1,130 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs/promises');
4
+ const path = require('path');
5
+
6
+ // ─── Helpers ────────────────────────────────────────────────────────────────
7
+
8
+ /**
9
+ * Read all .md files from a directory and return their concatenated content.
10
+ * @param {string} dir
11
+ * @returns {Promise<string>}
12
+ */
13
+ async function readMarkdownDir(dir) {
14
+ let content = '';
15
+ try {
16
+ const entries = await fs.readdir(dir, { withFileTypes: true });
17
+ for (const entry of entries) {
18
+ if (entry.isFile() && entry.name.endsWith('.md')) {
19
+ const filePath = path.join(dir, entry.name);
20
+ const fileContent = await fs.readFile(filePath, 'utf-8');
21
+ const section = entry.name.replace('.md', '').replace(/-/g, ' ');
22
+ content += `\n## ${section}\n\n${fileContent}\n\n---\n`;
23
+ }
24
+ }
25
+ } catch {
26
+ // Directory may not exist; silently skip
27
+ }
28
+ return content;
29
+ }
30
+
31
+ /**
32
+ * Ensure a directory exists, creating it if necessary.
33
+ * @param {string} dirPath
34
+ */
35
+ async function ensureDir(dirPath) {
36
+ await fs.mkdir(dirPath, { recursive: true });
37
+ }
38
+
39
+ /**
40
+ * Copy all files from a source directory to a destination directory.
41
+ * @param {string} src
42
+ * @param {string} dest
43
+ */
44
+ async function copyDir(src, dest) {
45
+ await ensureDir(dest);
46
+ const entries = await fs.readdir(src, { withFileTypes: true });
47
+ for (const entry of entries) {
48
+ if (entry.isFile()) {
49
+ await fs.copyFile(
50
+ path.join(src, entry.name),
51
+ path.join(dest, entry.name)
52
+ );
53
+ }
54
+ }
55
+ }
56
+
57
+ // ─── Cursor ──────────────────────────────────────────────────────────────────
58
+
59
+ /**
60
+ * Sync .agent/rules/ → .cursor/rules/
61
+ * @param {string} agentDir
62
+ * @param {string} cwd
63
+ */
64
+ async function syncToCursor(agentDir, cwd) {
65
+ const src = path.join(agentDir, 'rules');
66
+ const dest = path.join(cwd, '.cursor', 'rules');
67
+ await copyDir(src, dest);
68
+ }
69
+
70
+ // ─── Claude ──────────────────────────────────────────────────────────────────
71
+
72
+ /**
73
+ * Compile .agent/ into a single CLAUDE.md file.
74
+ * @param {string} agentDir
75
+ * @param {string} cwd
76
+ * @param {string} techStack
77
+ */
78
+ async function syncToClaude(agentDir, cwd, techStack) {
79
+ const rulesContent = await readMarkdownDir(path.join(agentDir, 'rules'));
80
+ const skillsContent = await readMarkdownDir(path.join(agentDir, 'skills'));
81
+
82
+ const claudeMd = `# CLAUDE.md — AI Agent Instructions
83
+
84
+ > This file was auto-generated by [agentive](https://github.com/TiPS0/agentive).
85
+ > Edit the source files in \`.agent/\` and re-run \`npx @p_tipso/agentive\` to regenerate.
86
+
87
+ ## Project Context
88
+
89
+ **Tech Stack:** ${techStack}
90
+
91
+ # Rules
92
+ ${rulesContent}
93
+
94
+ # Skills
95
+ ${skillsContent}
96
+ `.trim();
97
+
98
+ await fs.writeFile(path.join(cwd, 'CLAUDE.md'), claudeMd, 'utf-8');
99
+ }
100
+
101
+ // ─── Windsurf ────────────────────────────────────────────────────────────────
102
+
103
+ /**
104
+ * Compile .agent/rules/ into a single .windsurfrules file.
105
+ * @param {string} agentDir
106
+ * @param {string} cwd
107
+ * @param {string} techStack
108
+ */
109
+ async function syncToWindsurf(agentDir, cwd, techStack) {
110
+ const rulesContent = await readMarkdownDir(path.join(agentDir, 'rules'));
111
+ const skillsContent = await readMarkdownDir(path.join(agentDir, 'skills'));
112
+
113
+ const windsurfRules = `# .windsurfrules — AI Agent Instructions
114
+
115
+ > Auto-generated by [agentive](https://github.com/TiPS0/agentive).
116
+ > Edit \`.agent/\` and re-run \`npx @p_tipso/agentive\` to regenerate.
117
+
118
+ **Tech Stack:** ${techStack}
119
+
120
+ # Rules
121
+ ${rulesContent}
122
+
123
+ # Skills
124
+ ${skillsContent}
125
+ `.trim();
126
+
127
+ await fs.writeFile(path.join(cwd, '.windsurfrules'), windsurfRules, 'utf-8');
128
+ }
129
+
130
+ module.exports = { syncToCursor, syncToClaude, syncToWindsurf };
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs/promises');
4
+ const path = require('path');
5
+
6
+ // ─── Guards ──────────────────────────────────────────────────────────────────
7
+
8
+ /**
9
+ * Check if a .agent/ directory already exists in the given working directory.
10
+ * @param {string} cwd
11
+ * @returns {Promise<boolean>}
12
+ */
13
+ async function agentDirectoryExists(cwd) {
14
+ try {
15
+ await fs.access(path.join(cwd, '.agent'));
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ // ─── Directory Creation ───────────────────────────────────────────────────────
23
+
24
+ /**
25
+ * Create (or recreate) the .agent/ directory structure inside the user's project.
26
+ *
27
+ * Structure:
28
+ * .agent/
29
+ * ├── settings.json
30
+ * ├── settings.local.json
31
+ * ├── rules/
32
+ * └── skills/
33
+ *
34
+ * @param {string} cwd - The user's current working directory
35
+ * @param {boolean} overwrite - Whether to remove an existing .agent/ directory first
36
+ * @returns {Promise<string>} The path to the created .agent/ directory
37
+ */
38
+ async function createAgentDirectory(cwd, overwrite = false) {
39
+ const agentDir = path.join(cwd, '.agent');
40
+
41
+ if (overwrite) {
42
+ await fs.rm(agentDir, { recursive: true, force: true });
43
+ }
44
+
45
+ await fs.mkdir(agentDir, { recursive: true });
46
+ await fs.mkdir(path.join(agentDir, 'rules'), { recursive: true });
47
+ await fs.mkdir(path.join(agentDir, 'skills'), { recursive: true });
48
+
49
+ return agentDir;
50
+ }
51
+
52
+ // ─── Settings Files ───────────────────────────────────────────────────────────
53
+
54
+ /**
55
+ * Write settings.json and settings.local.json inside .agent/.
56
+ *
57
+ * - settings.json → tracked by git (project-wide config)
58
+ * - settings.local.json → gitignored (machine-specific overrides)
59
+ *
60
+ * Also appends `.agent/settings.local.json` to the project's .gitignore if one exists.
61
+ *
62
+ * @param {string} agentDir - Absolute path to .agent/
63
+ * @param {object} settings - The wizard answers to persist
64
+ */
65
+ async function writeSettings(agentDir, settings) {
66
+ // settings.json — committed to git
67
+ const settingsJson = {
68
+ projectName: settings.projectName,
69
+ techStack: settings.techStack,
70
+ tools: settings.tools,
71
+ agentiveVersion: settings.agentiveVersion,
72
+ createdAt: settings.createdAt,
73
+ };
74
+ await fs.writeFile(
75
+ path.join(agentDir, 'settings.json'),
76
+ JSON.stringify(settingsJson, null, 2) + '\n',
77
+ 'utf-8'
78
+ );
79
+
80
+ // settings.local.json — NOT committed (machine-specific overrides)
81
+ const localSettingsJson = {
82
+ _note: 'This file is gitignored. Add local machine-specific overrides here.',
83
+ overrides: {},
84
+ };
85
+ await fs.writeFile(
86
+ path.join(agentDir, 'settings.local.json'),
87
+ JSON.stringify(localSettingsJson, null, 2) + '\n',
88
+ 'utf-8'
89
+ );
90
+
91
+ // Auto-append to .gitignore if it exists in cwd
92
+ const cwd = path.dirname(agentDir);
93
+ const gitignorePath = path.join(cwd, '.gitignore');
94
+ try {
95
+ let gitignore = await fs.readFile(gitignorePath, 'utf-8');
96
+ const entry = '.agent/settings.local.json';
97
+ if (!gitignore.includes(entry)) {
98
+ gitignore += `\n# Agentive local settings (machine-specific)\n${entry}\n`;
99
+ await fs.writeFile(gitignorePath, gitignore, 'utf-8');
100
+ }
101
+ } catch {
102
+ // No .gitignore present — silently skip
103
+ }
104
+ }
105
+
106
+ // ─── Template Copying ─────────────────────────────────────────────────────────
107
+
108
+ /**
109
+ * Copy all template files from src/templates/ into the .agent/ directory.
110
+ * Replaces {{TECH_STACK}} and {{YEAR}} placeholders in template files.
111
+ *
112
+ * Note: The context/ folder from templates is not copied (not part of .agent/ structure).
113
+ *
114
+ * @param {string} templatesDir - Absolute path to src/templates/
115
+ * @param {string} destDir - Absolute path to .agent/
116
+ * @param {string} techStack - The user's tech stack string
117
+ */
118
+ async function copyTemplates(templatesDir, destDir, techStack) {
119
+ // Only copy rules/ and skills/ — context/ is dropped from .agent/ structure
120
+ const foldersToInclude = ['rules', 'skills'];
121
+
122
+ for (const folder of foldersToInclude) {
123
+ const srcFolder = path.join(templatesDir, folder);
124
+ const destFolder = path.join(destDir, folder);
125
+
126
+ try {
127
+ await fs.access(srcFolder); // skip if template folder doesn't exist
128
+ } catch {
129
+ continue;
130
+ }
131
+
132
+ await copyDirectoryRecursive(srcFolder, destFolder, techStack);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Internal: recursively copies files from src to dest, replacing placeholders.
138
+ * @param {string} src
139
+ * @param {string} dest
140
+ * @param {string} techStack
141
+ */
142
+ async function copyDirectoryRecursive(src, dest, techStack) {
143
+ const entries = await fs.readdir(src, { withFileTypes: true });
144
+
145
+ for (const entry of entries) {
146
+ const srcPath = path.join(src, entry.name);
147
+ const destPath = path.join(dest, entry.name);
148
+
149
+ if (entry.isDirectory()) {
150
+ await fs.mkdir(destPath, { recursive: true });
151
+ await copyDirectoryRecursive(srcPath, destPath, techStack);
152
+ } else {
153
+ let content = await fs.readFile(srcPath, 'utf-8');
154
+ content = content
155
+ .replace(/\{\{TECH_STACK\}\}/g, techStack)
156
+ .replace(/\{\{YEAR\}\}/g, new Date().getFullYear().toString());
157
+ await fs.writeFile(destPath, content, 'utf-8');
158
+ }
159
+ }
160
+ }
161
+
162
+ module.exports = {
163
+ agentDirectoryExists,
164
+ createAgentDirectory,
165
+ writeSettings,
166
+ copyTemplates,
167
+ };