@pratikpsl/agent-skills 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.
Files changed (45) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +92 -0
  3. package/bin/agent-skills.js +7 -0
  4. package/package.json +37 -0
  5. package/src/__tests__/copySkills.test.js +161 -0
  6. package/src/__tests__/list.test.js +49 -0
  7. package/src/__tests__/manifest.test.js +72 -0
  8. package/src/commands/add.js +89 -0
  9. package/src/commands/dotnet-setup.js +67 -0
  10. package/src/commands/init.js +42 -0
  11. package/src/commands/list.js +41 -0
  12. package/src/index.js +33 -0
  13. package/src/lib/copySkills.js +84 -0
  14. package/src/lib/manifest.js +117 -0
  15. package/src/lib/resolvePack.js +44 -0
  16. package/src/templates/dotnet/AgentSkills/OPERATING.md +85 -0
  17. package/src/templates/dotnet/AgentSkills/README.md +64 -0
  18. package/src/templates/dotnet/AgentSkills/agents/README.md +16 -0
  19. package/src/templates/dotnet/AgentSkills/agents/architect.agent.md +59 -0
  20. package/src/templates/dotnet/AgentSkills/agents/developer.agent.md +88 -0
  21. package/src/templates/dotnet/AgentSkills/entry-points/AGENTS.md +16 -0
  22. package/src/templates/dotnet/AgentSkills/entry-points/CLAUDE.md +16 -0
  23. package/src/templates/dotnet/AgentSkills/entry-points/copilot-instructions.md +16 -0
  24. package/src/templates/dotnet/AgentSkills/entry-points/cursor-instructions.md +16 -0
  25. package/src/templates/dotnet/AgentSkills/memory/index.md +22 -0
  26. package/src/templates/dotnet/AgentSkills/memory/lessons/api.md +3 -0
  27. package/src/templates/dotnet/AgentSkills/memory/lessons/auth.md +3 -0
  28. package/src/templates/dotnet/AgentSkills/memory/lessons/csharp.md +3 -0
  29. package/src/templates/dotnet/AgentSkills/memory/lessons/db.md +3 -0
  30. package/src/templates/dotnet/AgentSkills/memory/lessons/design.md +3 -0
  31. package/src/templates/dotnet/AgentSkills/memory/lessons/infra.md +7 -0
  32. package/src/templates/dotnet/AgentSkills/memory/lessons/mcp.md +3 -0
  33. package/src/templates/dotnet/AgentSkills/memory/lessons/testing.md +3 -0
  34. package/src/templates/dotnet/AgentSkills/memory/schema.md +26 -0
  35. package/src/templates/dotnet/AgentSkills/skills/INDEX.md +21 -0
  36. package/src/templates/dotnet/AgentSkills/skills/README.md +14 -0
  37. package/src/templates/dotnet/AgentSkills/skills/code-standards/SKILL.md +122 -0
  38. package/src/templates/dotnet/AgentSkills/skills/core/SKILL.md +83 -0
  39. package/src/templates/dotnet/AgentSkills/skills/csharp-xunit/SKILL.md +68 -0
  40. package/src/templates/dotnet/AgentSkills/skills/design/SKILL.md +108 -0
  41. package/src/templates/dotnet/AgentSkills/skills/dotnet-api/SKILL.md +303 -0
  42. package/src/templates/dotnet/AgentSkills/skills/dotnet-best-practices/SKILL.md +170 -0
  43. package/src/templates/dotnet/AgentSkills/skills/harness/SKILL.md +62 -0
  44. package/src/templates/dotnet/AgentSkills/skills/mcp_dotnet/SKILL.md +54 -0
  45. package/src/templates/dotnet/manifest.json +86 -0
@@ -0,0 +1,89 @@
1
+ // src/commands/add.js
2
+ // `agent-skills add <pack> <skillName|--all>`
3
+ //
4
+ // Copies a single named skill (or all skills with --all) from a pack into the
5
+ // current project's AgentSkills/ folder.
6
+ //
7
+ // This is the generic command. `dotnet-setup` is a convenience wrapper over this.
8
+
9
+ import path from 'path';
10
+ import { resolvePack } from '../lib/resolvePack.js';
11
+ import { readManifest } from '../lib/manifest.js';
12
+ import { copySkills, ensureAgentSkillsDir } from '../lib/copySkills.js';
13
+
14
+ /** @param {import('commander').Command} program */
15
+ export function addCommand(program) {
16
+ program
17
+ .command('add <pack> [skillName]')
18
+ .description('Copy a skill (or all skills with --all) from a pack into this project')
19
+ .option('--all', 'Copy all skills from the pack', false)
20
+ .option('--force', 'Overwrite existing files', false)
21
+ .option('--path <dir>', 'Target project root (default: cwd)', process.cwd())
22
+ .action(async (pack, skillName, options) => {
23
+ if (!skillName && !options.all) {
24
+ console.error('✗ Specify a skill name or pass --all to copy every skill.');
25
+ process.exit(1);
26
+ }
27
+
28
+ const targetDir = path.resolve(options.path);
29
+
30
+ let packDir;
31
+ try {
32
+ packDir = await resolvePack(pack);
33
+ } catch (err) {
34
+ console.error(`✗ ${err.message}`);
35
+ process.exit(1);
36
+ }
37
+
38
+ let manifest;
39
+ try {
40
+ manifest = readManifest(packDir);
41
+ } catch (err) {
42
+ console.error(`✗ ${err.message}`);
43
+ process.exit(1);
44
+ }
45
+
46
+ let skillsToCopy;
47
+ if (options.all) {
48
+ skillsToCopy = manifest.skills;
49
+ } else {
50
+ const found = manifest.skills.find((s) => s.name === skillName);
51
+ if (!found) {
52
+ const available = manifest.skills.map((s) => s.name).join(', ');
53
+ console.error(`✗ Skill "${skillName}" not found in pack "${pack}".`);
54
+ console.error(` Available: ${available}`);
55
+ process.exit(1);
56
+ }
57
+ skillsToCopy = [found];
58
+ }
59
+
60
+ ensureAgentSkillsDir(targetDir);
61
+ console.log(`\nCopying from ${manifest.name}@${manifest.version} → ${targetDir}\n`);
62
+
63
+ const { written, skipped } = copySkills({
64
+ skills: skillsToCopy,
65
+ packDir,
66
+ targetDir,
67
+ force: options.force,
68
+ });
69
+
70
+ printSummary(written, skipped);
71
+
72
+ if (written.length === 0 && skipped.length > 0) {
73
+ // Everything was skipped — treat as a soft warning, not a hard error.
74
+ process.exit(0);
75
+ }
76
+ });
77
+ }
78
+
79
+ /**
80
+ * @param {string[]} written
81
+ * @param {string[]} skipped
82
+ */
83
+ function printSummary(written, skipped) {
84
+ console.log('\n' + '─'.repeat(50));
85
+ console.log(`✔ Done — ${written.length} written, ${skipped.length} skipped.`);
86
+ if (skipped.length > 0) {
87
+ console.log(' (Run with --force to overwrite skipped files.)');
88
+ }
89
+ }
@@ -0,0 +1,67 @@
1
+ // src/commands/dotnet-setup.js
2
+ // `agent-skills dotnet-setup`
3
+ //
4
+ // Convenience command equivalent to:
5
+ // agent-skills add @pratikpsl/agent-skills-dotnet --all
6
+ //
7
+ // Rationale: gives first-time users a single, memorable command for the .NET
8
+ // stack without requiring them to know the full pack name.
9
+
10
+ import path from 'path';
11
+ import { resolvePack } from '../lib/resolvePack.js';
12
+ import { readManifest } from '../lib/manifest.js';
13
+ import { copySkills, ensureAgentSkillsDir } from '../lib/copySkills.js';
14
+
15
+ const DOTNET_PACK = '@pratikpsl/agent-skills-dotnet';
16
+
17
+ /** @param {import('commander').Command} program */
18
+ export function dotnetSetupCommand(program) {
19
+ program
20
+ .command('dotnet-setup')
21
+ .description(`Install the full .NET/C# agent skill set (shortcut for: add ${DOTNET_PACK} --all)`)
22
+ .option('--force', 'Overwrite existing files', false)
23
+ .option('--path <dir>', 'Target project root (default: cwd)', process.cwd())
24
+ .action(async (options) => {
25
+ const targetDir = path.resolve(options.path);
26
+
27
+ console.log(`\n🔧 Setting up .NET agent skills in: ${targetDir}\n`);
28
+
29
+ let packDir;
30
+ try {
31
+ packDir = await resolvePack(DOTNET_PACK);
32
+ } catch (err) {
33
+ console.error(`✗ ${err.message}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ let manifest;
38
+ try {
39
+ manifest = readManifest(packDir);
40
+ } catch (err) {
41
+ console.error(`✗ ${err.message}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ ensureAgentSkillsDir(targetDir);
46
+ console.log(`Copying from ${manifest.name}@${manifest.version}\n`);
47
+
48
+ const { written, skipped } = copySkills({
49
+ skills: manifest.skills,
50
+ packDir,
51
+ targetDir,
52
+ force: options.force,
53
+ });
54
+
55
+ console.log('\n' + '─'.repeat(50));
56
+ console.log(`✔ .NET skills installed — ${written.length} written, ${skipped.length} skipped.`);
57
+
58
+ if (skipped.length > 0) {
59
+ console.log(' (Run with --force to overwrite skipped files.)');
60
+ }
61
+
62
+ if (written.length === 0 && skipped.length === 0) {
63
+ console.error('\n✗ No skills were copied. Check that the pack contains skill entries.');
64
+ process.exit(1);
65
+ }
66
+ });
67
+ }
@@ -0,0 +1,42 @@
1
+ // src/commands/init.js
2
+ // `agent-skills init`
3
+ //
4
+ // Scaffolds an empty AgentSkills/ folder + index.json in the current project
5
+ // if one doesn't exist yet. Safe to run multiple times (idempotent).
6
+
7
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
8
+ import path from 'path';
9
+
10
+ /** @param {import('commander').Command} program */
11
+ export function initCommand(program) {
12
+ program
13
+ .command('init')
14
+ .description('Scaffold an empty AgentSkills/ folder in the current project')
15
+ .option('--path <dir>', 'Target project root (default: cwd)', process.cwd())
16
+ .option('--force', 'Overwrite existing files', false)
17
+ .action(async (options) => {
18
+ const targetDir = path.resolve(options.path);
19
+ const agentSkillsDir = path.join(targetDir, 'AgentSkills');
20
+
21
+ if (existsSync(agentSkillsDir) && !options.force) {
22
+ console.log(`AgentSkills/ already exists at ${agentSkillsDir}`);
23
+ console.log('Use --force to reinitialize.');
24
+ process.exit(0);
25
+ }
26
+
27
+ mkdirSync(agentSkillsDir, { recursive: true });
28
+ console.log(`✓ Created ${agentSkillsDir}`);
29
+
30
+ const indexPath = path.join(agentSkillsDir, 'index.json');
31
+ if (!existsSync(indexPath) || options.force) {
32
+ const indexContent = {
33
+ skills: [],
34
+ _comment: 'Add skills via `agent-skills add <pack> <skillName>` or `agent-skills dotnet-setup`',
35
+ };
36
+ writeFileSync(indexPath, JSON.stringify(indexContent, null, 2) + '\n', 'utf8');
37
+ console.log(`✓ Created ${indexPath}`);
38
+ }
39
+
40
+ console.log('\n✔ AgentSkills initialized. Run `agent-skills dotnet-setup` to add .NET skills.');
41
+ });
42
+ }
@@ -0,0 +1,41 @@
1
+ // src/commands/list.js
2
+ // `agent-skills list [pack]`
3
+ //
4
+ // Lists available skills in an installed or available pack.
5
+ // When no pack is specified, lists all packs the CLI knows about.
6
+
7
+ import { resolvePack } from '../lib/resolvePack.js';
8
+ import { readManifest } from '../lib/manifest.js';
9
+
10
+ /** @param {import('commander').Command} program */
11
+ export function listCommand(program) {
12
+ program
13
+ .command('list [pack]')
14
+ .description('List available skills in a pack (default: all known packs)')
15
+ .action(async (pack) => {
16
+ const packsToList = pack
17
+ ? [pack]
18
+ : ['dotnet']; // Extend this array as new packs are added.
19
+
20
+ let exitCode = 0;
21
+
22
+ for (const packName of packsToList) {
23
+ try {
24
+ const packDir = await resolvePack(packName);
25
+ const manifest = readManifest(packDir);
26
+
27
+ console.log(`\n📦 ${manifest.name}@${manifest.version}`);
28
+ console.log('─'.repeat(50));
29
+
30
+ for (const skill of manifest.skills) {
31
+ console.log(` • ${skill.name.padEnd(28)} ${skill.description}`);
32
+ }
33
+ } catch (err) {
34
+ console.error(`\n✗ Could not list pack "${packName}": ${err.message}`);
35
+ exitCode = 1;
36
+ }
37
+ }
38
+
39
+ if (exitCode !== 0) process.exit(exitCode);
40
+ });
41
+ }
package/src/index.js ADDED
@@ -0,0 +1,33 @@
1
+ // src/index.js
2
+ // Wires up the root `commander` program and registers all sub-commands.
3
+ // This file is the only place that imports commander; sub-commands receive
4
+ // the program instance so they stay independently testable.
5
+
6
+ import { Command } from 'commander';
7
+ import { createRequire } from 'module';
8
+ import { initCommand } from './commands/init.js';
9
+ import { listCommand } from './commands/list.js';
10
+ import { addCommand } from './commands/add.js';
11
+ import { dotnetSetupCommand } from './commands/dotnet-setup.js';
12
+
13
+ // Read version from our own package.json without a JSON import assertion
14
+ // (works in Node 18 without the --experimental-json-modules flag).
15
+ const require = createRequire(import.meta.url);
16
+ const { version } = require('../package.json');
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('agent-skills')
22
+ .description('Scaffold and manage AI agent skills across projects')
23
+ .version(version);
24
+
25
+ initCommand(program);
26
+ listCommand(program);
27
+ addCommand(program);
28
+ dotnetSetupCommand(program);
29
+
30
+ program.parseAsync(process.argv).catch((err) => {
31
+ console.error(`\n✗ ${err.message}`);
32
+ process.exit(1);
33
+ });
@@ -0,0 +1,84 @@
1
+ // src/lib/copySkills.js
2
+ // Copies skill files from a pack into the target project's AgentSkills/ folder.
3
+ //
4
+ // Rules enforced here:
5
+ // - Never overwrite an existing file without --force; warn and skip instead.
6
+ // - Directories are created recursively as needed.
7
+ // - Returns a summary object for the caller to print: { written, skipped }.
8
+ //
9
+ // This module contains no pack-specific logic. The manifest tells it what to copy;
10
+ // it does not inspect SKILL.md contents.
11
+
12
+ import { cpSync, existsSync, mkdirSync, rmSync } from 'fs';
13
+ import path from 'path';
14
+
15
+ /**
16
+ * @typedef {Object} CopyResult
17
+ * @property {string[]} written Files that were successfully written.
18
+ * @property {string[]} skipped Files that were skipped because they already exist.
19
+ */
20
+
21
+ /**
22
+ * Copies a list of skill source directories into the target project.
23
+ *
24
+ * @param {object} options
25
+ * @param {import('./manifest.js').SkillEntry[]} options.skills
26
+ * Skills to copy, filtered from the manifest (all or a single skill).
27
+ * @param {string} options.packDir Absolute path to the pack root.
28
+ * @param {string} options.targetDir Absolute path to the target project root.
29
+ * @param {boolean} options.force When true, overwrite existing files.
30
+ * @returns {CopyResult}
31
+ */
32
+ export function copySkills({ skills, packDir, targetDir, force }) {
33
+ /** @type {string[]} */
34
+ const written = [];
35
+ /** @type {string[]} */
36
+ const skipped = [];
37
+
38
+ for (const skill of skills) {
39
+ const src = path.join(packDir, skill.path);
40
+ // dest overrides where the file lands in the target project.
41
+ // Used for IDE entry-points (e.g. CLAUDE.md, .github/copilot-instructions.md)
42
+ // that must live outside AgentSkills/ at a specific root-relative path.
43
+ const destRelPath = skill.dest ?? skill.path;
44
+ const dest = path.join(targetDir, destRelPath);
45
+
46
+ if (!existsSync(src)) {
47
+ console.warn(` ⚠ Source path for skill "${skill.name}" not found: ${src}`);
48
+ skipped.push(destRelPath);
49
+ continue;
50
+ }
51
+
52
+ if (existsSync(dest) && !force) {
53
+ console.warn(
54
+ ` ⚠ Skipping "${skill.name}" — destination already exists. Use --force to overwrite.`
55
+ );
56
+ skipped.push(destRelPath);
57
+ continue;
58
+ }
59
+
60
+ mkdirSync(path.dirname(dest), { recursive: true });
61
+ // Remove the destination first when forcing — cpSync throws "src and dest cannot
62
+ // be the same" on Windows if the dest directory already exists, even with force:true.
63
+ if (force && existsSync(dest)) {
64
+ rmSync(dest, { recursive: true, force: true });
65
+ }
66
+ cpSync(src, dest, { recursive: true });
67
+ written.push(destRelPath);
68
+ console.log(` ✓ ${destRelPath}`);
69
+ }
70
+
71
+ return { written, skipped };
72
+ }
73
+
74
+ /**
75
+ * Ensures the AgentSkills root directory exists in the target project.
76
+ *
77
+ * @param {string} targetDir Absolute path to the target project root.
78
+ * @returns {string} Absolute path to AgentSkills/.
79
+ */
80
+ export function ensureAgentSkillsDir(targetDir) {
81
+ const dir = path.join(targetDir, 'AgentSkills');
82
+ mkdirSync(dir, { recursive: true });
83
+ return dir;
84
+ }
@@ -0,0 +1,117 @@
1
+ // src/lib/manifest.js
2
+ // Reads and validates a pack's manifest.json.
3
+ //
4
+ // The manifest is the contract between a skills pack and the CLI.
5
+ // It declares what skills exist, where their source files are, and where
6
+ // they should be placed in the target project.
7
+ //
8
+ // Expected shape:
9
+ // {
10
+ // "name": "@pratikpsl/agent-skills-dotnet",
11
+ // "version": "0.1.0",
12
+ // "skills": [
13
+ // {
14
+ // "name": "core",
15
+ // "description": "...",
16
+ // "path": "AgentSkills/skills/core" // relative to pack root
17
+ // }
18
+ // ]
19
+ // }
20
+
21
+ import { readFileSync, existsSync } from 'fs';
22
+ import path from 'path';
23
+
24
+ /**
25
+ * @typedef {Object} SkillEntry
26
+ * @property {string} name Short identifier used as a CLI argument.
27
+ * @property {string} description Human-readable purpose of the skill.
28
+ * @property {string} path Source path of the skill, relative to the pack root.
29
+ * @property {string} [dest] Destination path in the target project, relative to the
30
+ * project root. When absent, `path` is used as the destination.
31
+ * Use this for IDE entry-point files that must land outside
32
+ * AgentSkills/ (e.g. CLAUDE.md, .github/copilot-instructions.md).
33
+ */
34
+
35
+ /**
36
+ * @typedef {Object} Manifest
37
+ * @property {string} name Package name.
38
+ * @property {string} version Semver string.
39
+ * @property {SkillEntry[]} skills All skills provided by this pack.
40
+ */
41
+
42
+ /**
43
+ * Reads and validates the manifest.json for a pack.
44
+ *
45
+ * @param {string} packDir Absolute path to the pack root.
46
+ * @returns {Manifest}
47
+ * @throws {Error} When manifest is missing, unreadable, or structurally invalid.
48
+ */
49
+ export function readManifest(packDir) {
50
+ const manifestPath = path.join(packDir, 'manifest.json');
51
+
52
+ if (!existsSync(manifestPath)) {
53
+ throw new Error(
54
+ `manifest.json not found in pack at "${packDir}". ` +
55
+ `This pack may be malformed or out of date.`
56
+ );
57
+ }
58
+
59
+ let raw;
60
+ try {
61
+ raw = readFileSync(manifestPath, 'utf8');
62
+ if (raw.charCodeAt(0) === 0xFEFF) {
63
+ raw = raw.slice(1);
64
+ }
65
+ } catch (err) {
66
+ throw new Error(`Could not read manifest.json: ${err.message}`);
67
+ }
68
+
69
+ let manifest;
70
+ try {
71
+ manifest = JSON.parse(raw);
72
+ } catch (err) {
73
+ throw new Error(`manifest.json at "${manifestPath}" is not valid JSON: ${err.message}`);
74
+ }
75
+
76
+ validateManifest(manifest, manifestPath);
77
+ return manifest;
78
+ }
79
+
80
+ /**
81
+ * Throws with a descriptive message if the manifest does not meet the required shape.
82
+ *
83
+ * @param {unknown} manifest
84
+ * @param {string} filePath Used only in error messages.
85
+ */
86
+ function validateManifest(manifest, filePath) {
87
+ if (typeof manifest !== 'object' || manifest === null) {
88
+ throw new Error(`manifest.json at "${filePath}" must be a JSON object.`);
89
+ }
90
+
91
+ const required = ['name', 'version', 'skills'];
92
+ for (const key of required) {
93
+ if (!(key in manifest)) {
94
+ throw new Error(`manifest.json is missing required field "${key}".`);
95
+ }
96
+ }
97
+
98
+ if (!Array.isArray(manifest.skills)) {
99
+ throw new Error(`manifest.json "skills" must be an array.`);
100
+ }
101
+
102
+ for (const [i, skill] of manifest.skills.entries()) {
103
+ for (const field of ['name', 'description', 'path']) {
104
+ if (typeof skill[field] !== 'string' || !skill[field].trim()) {
105
+ throw new Error(
106
+ `manifest.json skills[${i}] is missing or has an empty "${field}" field.`
107
+ );
108
+ }
109
+ }
110
+ // dest is optional; when present it must be a non-empty string.
111
+ if ('dest' in skill && (typeof skill.dest !== 'string' || !skill.dest.trim())) {
112
+ throw new Error(
113
+ `manifest.json skills[${i}] has an invalid "dest" field — must be a non-empty string when present.`
114
+ );
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,44 @@
1
+ // src/lib/resolvePack.js
2
+ // Locates a templates folder bundled inside the package itself.
3
+ // Since everything is now in one package, we don't need to fetch
4
+ // anything from npm registry at runtime.
5
+
6
+ import { fileURLToPath } from 'url';
7
+ import { existsSync } from 'fs';
8
+ import path from 'path';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ /**
14
+ * Canonical package names for built-in packs.
15
+ * @type {Record<string, string>}
16
+ */
17
+ const PACK_ALIASES = {
18
+ dotnet: 'dotnet',
19
+ 'skills-dotnet': 'dotnet',
20
+ '@pratikpsl/agent-skills-dotnet': 'dotnet',
21
+ };
22
+
23
+ /**
24
+ * Resolves a pack name or alias to its installed directory.
25
+ *
26
+ * @param {string} packName Short alias (e.g. "dotnet") or full package name.
27
+ * @returns {Promise<string>} Absolute path to the pack root.
28
+ * @throws {Error} When the pack cannot be located.
29
+ */
30
+ export async function resolvePack(packName) {
31
+ const normalized = PACK_ALIASES[packName] ?? packName.replace(/^@pratikpsl\/agent-skills-/, '').replace(/^skills-/, '');
32
+
33
+ // Locate the templates folder inside the package src directory
34
+ const templateDir = path.resolve(__dirname, '..', 'templates', normalized);
35
+
36
+ if (existsSync(templateDir) && existsSync(path.join(templateDir, 'manifest.json'))) {
37
+ return templateDir;
38
+ }
39
+
40
+ throw new Error(
41
+ `Pack "${packName}" (mapped to "${normalized}") is not bundled with this version of the CLI. ` +
42
+ `Currently available packs: dotnet`
43
+ );
44
+ }
@@ -0,0 +1,85 @@
1
+ # Coding Agent Operating Contract
2
+
3
+ This file is the canonical prerequisite and completion workflow for every coding
4
+ agent and AI-enabled IDE working in this repository.
5
+
6
+ Tool-specific files such as `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md`,
7
+ `.agent/rules/instructions.md`, `.copilot/`, and `.codex/` are lightweight entry
8
+ points only. They must route agents here and must not duplicate skill, agent,
9
+ memory, checklist, or project-rule content.
10
+
11
+ ## Applies To
12
+
13
+ This contract applies to Codex, GitHub Copilot, Claude, Cursor, Antigravity,
14
+ local agent harnesses, CI hooks, and any future coding agent integration.
15
+
16
+ ## Task Start Prerequisites
17
+
18
+ Before changing files or producing a final answer, every agent must complete this
19
+ bootstrap sequence:
20
+
21
+ 1. Read `AgentSkills/memory/index.md`, then load only the domain lesson file(s)
22
+ that match the task.
23
+ 2. Read `AgentSkills/skills/INDEX.md`.
24
+ 3. Always load `AgentSkills/skills/core/SKILL.md`.
25
+ 4. Load any task-specific skill listed in the skill index.
26
+ 5. Load `AgentSkills/skills/harness/SKILL.md` when changing instructions,
27
+ validation scripts, CI workflows, hooks, or operational guardrails.
28
+ 6. Load agent definitions from `AgentSkills/agents/` only when the task needs a
29
+ role-specific agent:
30
+ - `AgentSkills/agents/architect.agent.md` for design, CQRS structure, SQL
31
+ schema, or MCP contract planning.
32
+ - `AgentSkills/agents/developer.agent.md` for .NET C# implementation work.
33
+ 7. Inspect the local repository context before editing, including relevant files
34
+ and current worktree state.
35
+ 8. Define the verification path for the task before making changes.
36
+
37
+ If the request is ambiguous and a safe, local assumption is not possible, ask a
38
+ short clarifying question before editing.
39
+
40
+ ## Working Rules
41
+
42
+ - Keep `AgentSkills/` as the single source of truth for skills, agents, memory,
43
+ prerequisites, project rules, and completion gates.
44
+ - Use repo-relative paths in shared documentation and configuration.
45
+ - Keep changes scoped to the user request and the files required to satisfy it.
46
+ - Preserve user changes already present in the worktree.
47
+ - Prefer the repository's existing patterns over new abstractions.
48
+ - Update public documentation only when behavior, setup, commands, or usage
49
+ changes.
50
+ - Record a memory lesson only when a mistake was made or a durable
51
+ project-specific rule was discovered.
52
+
53
+ ## Completion Gate
54
+
55
+ Before marking work complete, every coding agent must:
56
+
57
+ 1. Run the pre-submit checklist in `AgentSkills/skills/core/SKILL.md`.
58
+ 2. Run the shared harness sensor when code, project files, instructions, CI,
59
+ hooks, or validation behavior changed:
60
+
61
+ ```powershell
62
+ pwsh ./tools/Harness/validate.ps1
63
+ ```
64
+
65
+ 3. For quick local loops during development, the harness may be run in Debug
66
+ mode with formatting skipped, but the final pass should use the full command
67
+ unless the task is documentation-only or the environment cannot run it:
68
+
69
+ ```powershell
70
+ pwsh ./tools/Harness/validate.ps1 -Configuration Debug -SkipFormat
71
+ ```
72
+
73
+ 4. If the full harness cannot be run, report the exact command that was skipped
74
+ or failed and the reason.
75
+ 5. Do not claim completion when required checks fail. List failures clearly and
76
+ leave the work in a reviewable state.
77
+
78
+ ## Final Response
79
+
80
+ The final response should state:
81
+
82
+ - What changed.
83
+ - Which validation commands were run.
84
+ - Any failures, skipped checks, or follow-up risks.
85
+
@@ -0,0 +1,64 @@
1
+ # AgentSkills: Unified Skill & Memory Workspace
2
+
3
+ This directory is the single, shared source of truth for all AI agents (Claude, Codex, Copilot, Antigravity, etc.) interacting with this repository.
4
+
5
+ ---
6
+
7
+ ## Folder Structure
8
+
9
+ ```
10
+ AgentSkills/
11
+ ├── skills/ ← Custom developer skills (best practices, guidelines)
12
+ │ ├── INDEX.md ← Index of all skills and when to load them
13
+ │ ├── core/ ← Core principles (think before coding, checklist)
14
+ │ ├── code-standards/ ← Coding standards (no magic values, naming, DRY)
15
+ │ ├── design/ ← SOLID, KISS, YAGNI, and design pattern rules
16
+ │ ├── dotnet-best-practices/ ← General .NET and EF Core best practices
17
+ │ ├── csharp-xunit/ ← Unit testing guidelines using xUnit
18
+ │ ├── mcp_dotnet/ ← Model Context Protocol tool rules
19
+ │ ├── dotnet-api/ ← RESTful API URL structure, pipeline, and OpenAPI rules
20
+ │ └── harness/ ← Guide and sensor setup for agent operations
21
+
22
+ ├── agents/ ← Custom agent definition personas
23
+ │ ├── README.md ← Agent index and canonical path instructions
24
+ │ ├── architect.agent.md ← Structure and design planning agent
25
+ │ └── developer.agent.md ← C# / .NET implementation agent
26
+
27
+ └── memory/ ← Persistent agent memories
28
+ ├── index.md ← Router: always load this first; maps task type to domain file
29
+ ├── schema.md ← Lesson logging format (load only when writing a lesson)
30
+ └── lessons/
31
+ ├── api.md ← CQRS, MediatR, commands, queries, controllers
32
+ ├── db.md ← EF Core, migrations, schema, repositories
33
+ ├── infra.md ← Build, paths, CI, tooling, portability
34
+ ├── auth.md ← Authentication, authorisation, JWT
35
+ ├── testing.md ← xUnit, mocking, coverage
36
+ ├── csharp.md ← C# language features, naming, async/await
37
+ ├── design.md ← SOLID, CQRS, patterns, architecture decisions
38
+ └── mcp.md ← MCP server tools, transports, contracts
39
+ ```
40
+
41
+ ---
42
+
43
+ ## How It Works
44
+
45
+ 1. **Task Initialization**:
46
+ - The agent starts with [AgentSkills/OPERATING.md](OPERATING.md), the shared operating contract for all coding agents and AI-enabled IDEs.
47
+ - The agent reads [AgentSkills/memory/index.md](memory/index.md) to identify relevant domains, then loads only the matching domain file(s) from `memory/lessons/`.
48
+ - The agent reads [AgentSkills/skills/INDEX.md](skills/INDEX.md) to load `core` and any task-specific skills.
49
+
50
+ 2. **Core Checklist**:
51
+ - Before finishing any task, the agent runs the prerequisite and pre-submit checklists in [AgentSkills/skills/core/SKILL.md](skills/core/SKILL.md).
52
+
53
+ 3. **Memory Update**:
54
+ - If the agent makes a mistake or discovers a project-specific rule, it reads [memory/schema.md](memory/schema.md) for the one-line format, appends to the correct domain file, and increments the count in `index.md`.
55
+
56
+ 4. **Tool Entry Points**:
57
+ - Codex reads `AGENTS.md`.
58
+ - Claude reads `CLAUDE.md`.
59
+ - GitHub Copilot reads `.github/copilot-instructions.md`.
60
+ - Other coding IDEs and agents read their configured lightweight entry point.
61
+ - Tool-specific files point back to `AgentSkills/OPERATING.md`; skill, agent, memory, checklist, and project-rule content is not duplicated.
62
+
63
+ 5. **Integration Hooks**:
64
+ - A post-tool-use hook is configured in `.github/hooks/build_test_lint.json` to run the shared harness sensor at `tools/Harness/validate.ps1`.