@sprucelabs/spruce-cli 29.2.1 → 29.3.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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [29.3.0](https://github.com/sprucelabsai-community/spruce-cli-workspace/compare/v29.2.1...v29.3.0) (2026-01-13)
7
+
8
+
9
+ ### Features
10
+
11
+ * new setup.ai command to setup agent/claude/project.md docs ([e1273b1](https://github.com/sprucelabsai-community/spruce-cli-workspace/commit/e1273b1))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [29.2.1](https://github.com/sprucelabsai-community/spruce-cli-workspace/compare/v29.2.0...v29.2.1) (2026-01-12)
7
18
 
8
19
  **Note:** Version bump only for package @sprucelabs/spruce-cli
@@ -0,0 +1,12 @@
1
+ import AbstractSkillTest from '../../../tests/AbstractSkillTest';
2
+ export default class SettingUpForAiAssistedDevelopmentTest extends AbstractSkillTest {
3
+ protected static skillCacheKey: string;
4
+ private static readonly expectedFiles;
5
+ protected static hasSetupForAiDevelopmentAction(): Promise<void>;
6
+ protected static generatesExpectedFiles(): Promise<void>;
7
+ protected static generatedFilesHaveExpectedContents(): Promise<void>;
8
+ private static assertFileContentsMatch;
9
+ private static readTemplate;
10
+ private static assertExpectedFilesExist;
11
+ private static execute;
12
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
13
+ const test_utils_1 = require("@sprucelabs/test-utils");
14
+ const AbstractSkillTest_1 = __importDefault(require("../../../tests/AbstractSkillTest"));
15
+ class SettingUpForAiAssistedDevelopmentTest extends AbstractSkillTest_1.default {
16
+ static skillCacheKey = 'node';
17
+ static expectedFiles = [
18
+ 'CLAUDE.md',
19
+ 'AGENTS.md',
20
+ 'docs/PROJECT.md',
21
+ ];
22
+ static async hasSetupForAiDevelopmentAction() {
23
+ test_utils_1.assert.isFunction(this.Action('node', 'setupForAiDevelopment').execute);
24
+ }
25
+ static async generatesExpectedFiles() {
26
+ await this.execute();
27
+ this.assertExpectedFilesExist();
28
+ }
29
+ static async generatedFilesHaveExpectedContents() {
30
+ this.assertFileContentsMatch('CLAUDE.md', 'markdown/CLAUDE.md.hbs');
31
+ this.assertFileContentsMatch('AGENTS.md', 'markdown/AGENTS.md.hbs');
32
+ this.assertFileContentsMatch('docs/PROJECT.md', 'markdown/PROJECT.md.hbs');
33
+ }
34
+ static assertFileContentsMatch(generatedFile, templateFile) {
35
+ const generatedContents = spruce_skill_utils_1.diskUtil.readFile(this.resolvePath(generatedFile));
36
+ const templateContents = this.readTemplate(templateFile);
37
+ test_utils_1.assert.isEqual(generatedContents, templateContents);
38
+ }
39
+ static readTemplate(templatePath) {
40
+ const fullPath = spruce_skill_utils_1.diskUtil.resolvePath(__dirname, '..', '..', '..', '..', '..', 'spruce-templates', 'src', 'templates', templatePath);
41
+ return spruce_skill_utils_1.diskUtil.readFile(fullPath);
42
+ }
43
+ static assertExpectedFilesExist() {
44
+ for (const file of this.expectedFiles) {
45
+ test_utils_1.assert.isTrue(spruce_skill_utils_1.diskUtil.doesFileExist(this.resolvePath(file)), `Expected ${file} to exist`);
46
+ }
47
+ }
48
+ static async execute() {
49
+ const results = await this.Action('node', 'setupForAiDevelopment').execute({});
50
+ test_utils_1.assert.isFalsy(results.errors, 'Action should not return errors');
51
+ return results;
52
+ }
53
+ }
54
+ exports.default = SettingUpForAiAssistedDevelopmentTest;
55
+ __decorate([
56
+ (0, test_utils_1.test)()
57
+ ], SettingUpForAiAssistedDevelopmentTest, "hasSetupForAiDevelopmentAction", null);
58
+ __decorate([
59
+ (0, test_utils_1.test)()
60
+ ], SettingUpForAiAssistedDevelopmentTest, "generatesExpectedFiles", null);
61
+ __decorate([
62
+ (0, test_utils_1.test)()
63
+ ], SettingUpForAiAssistedDevelopmentTest, "generatedFilesHaveExpectedContents", null);
64
+ //# sourceMappingURL=SettingUpForAiAssistedDevelopment.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingUpForAiAssistedDevelopment.test.js","sourceRoot":"","sources":["../../../../src/__tests__/behavioral/agents/SettingUpForAiAssistedDevelopment.test.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,uEAAyD;AACzD,uDAAqD;AACrD,yFAAgE;AAEhE,MAAqB,qCAAsC,SAAQ,2BAAiB;IACtE,MAAM,CAAC,aAAa,GAAG,MAAM,CAAA;IAE/B,MAAM,CAAU,aAAa,GAAG;QACpC,WAAW;QACX,WAAW;QACX,iBAAiB;KACpB,CAAA;IAGsB,AAAb,MAAM,CAAC,KAAK,CAAC,8BAA8B;QACjD,mBAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,OAAO,CAAC,CAAA;IAC3E,CAAC;IAGsB,AAAb,MAAM,CAAC,KAAK,CAAC,sBAAsB;QACzC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACpB,IAAI,CAAC,wBAAwB,EAAE,CAAA;IACnC,CAAC;IAGsB,AAAb,MAAM,CAAC,KAAK,CAAC,kCAAkC;QACrD,IAAI,CAAC,uBAAuB,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAA;QACnE,IAAI,CAAC,uBAAuB,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAA;QACnE,IAAI,CAAC,uBAAuB,CACxB,iBAAiB,EACjB,yBAAyB,CAC5B,CAAA;IACL,CAAC;IAEO,MAAM,CAAC,uBAAuB,CAClC,aAAqB,EACrB,YAAoB;QAEpB,MAAM,iBAAiB,GAAG,6BAAQ,CAAC,QAAQ,CACvC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAClC,CAAA;QACD,MAAM,gBAAgB,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAA;QACxD,mBAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAA;IACvD,CAAC;IAEO,MAAM,CAAC,YAAY,CAAC,YAAoB;QAC5C,MAAM,QAAQ,GAAG,6BAAQ,CAAC,WAAW,CACjC,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,kBAAkB,EAClB,KAAK,EACL,WAAW,EACX,YAAY,CACf,CAAA;QACD,OAAO,6BAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACtC,CAAC;IAEO,MAAM,CAAC,wBAAwB;QACnC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACpC,mBAAM,CAAC,MAAM,CACT,6BAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAC9C,YAAY,IAAI,WAAW,CAC9B,CAAA;QACL,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,OAAO;QACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAC7B,MAAM,EACN,uBAAuB,CAC1B,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACb,mBAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAA;QACjE,OAAO,OAAO,CAAA;IAClB,CAAC;;AAzEL,wDA0EC;AAhE0B;IADtB,IAAA,iBAAI,GAAE;iFAGN;AAGsB;IADtB,IAAA,iBAAI,GAAE;yEAIN;AAGsB;IADtB,IAAA,iBAAI,GAAE;qFAQN"}
@@ -0,0 +1,19 @@
1
+ import { SchemaValues } from '@sprucelabs/schema';
2
+ import AbstractAction from '../../AbstractAction';
3
+ import { FeatureActionResponse } from '../../features.types';
4
+ declare const optionsSchema: {
5
+ id: string;
6
+ fields: {};
7
+ };
8
+ type OptionsSchema = typeof optionsSchema;
9
+ type Options = SchemaValues<OptionsSchema>;
10
+ export default class SetupForAiDevelopmentAction extends AbstractAction<OptionsSchema> {
11
+ optionsSchema: {
12
+ id: string;
13
+ fields: {};
14
+ };
15
+ commandAliases: string[];
16
+ invocationMessage: string;
17
+ execute(_options: Options): Promise<FeatureActionResponse>;
18
+ }
19
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const schema_1 = require("@sprucelabs/schema");
7
+ const AbstractAction_1 = __importDefault(require("../../AbstractAction"));
8
+ const optionsSchema = (0, schema_1.buildSchema)({
9
+ id: 'setupForAiDevelopmentOptions',
10
+ fields: {},
11
+ });
12
+ class SetupForAiDevelopmentAction extends AbstractAction_1.default {
13
+ optionsSchema = optionsSchema;
14
+ commandAliases = ['setup.ai'];
15
+ invocationMessage = 'Setting up for AI-assisted development... 🤖';
16
+ async execute(_options) {
17
+ const writer = this.Writer('node');
18
+ const files = await writer.writeAiDevelopmentFiles(this.cwd);
19
+ return { files };
20
+ }
21
+ }
22
+ exports.default = SetupForAiDevelopmentAction;
23
+ //# sourceMappingURL=SetupForAiDevelopmentAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SetupForAiDevelopmentAction.js","sourceRoot":"","sources":["../../../../src/features/node/actions/SetupForAiDevelopmentAction.ts"],"names":[],"mappings":";;;;;AAAA,+CAA8D;AAC9D,0EAAiD;AAGjD,MAAM,aAAa,GAAG,IAAA,oBAAW,EAAC;IAC9B,EAAE,EAAE,8BAA8B;IAClC,MAAM,EAAE,EAAE;CACb,CAAC,CAAA;AAKF,MAAqB,2BAA4B,SAAQ,wBAA6B;IAC3E,aAAa,GAAG,aAAa,CAAA;IAC7B,cAAc,GAAG,CAAC,UAAU,CAAC,CAAA;IAC7B,iBAAiB,GAAG,8CAA8C,CAAA;IAElE,KAAK,CAAC,OAAO,CAAC,QAAiB;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAClC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAE5D,OAAO,EAAE,KAAK,EAAE,CAAA;IACpB,CAAC;CACJ;AAXD,8CAWC"}
@@ -4,4 +4,5 @@ export default class NodeWriter extends AbstractWriter {
4
4
  writeNodeModule(destinationDir: string, options?: Partial<WriteDirectoryTemplateOptions> & {
5
5
  shouldWriteIndex?: boolean;
6
6
  }): Promise<WriteResults>;
7
+ writeAiDevelopmentFiles(destinationDir: string): Promise<import("@sprucelabs/mercury-types").SpruceSchemas.SpruceCli.v2020_07_22.GeneratedFile[]>;
7
8
  }
@@ -36,6 +36,12 @@ class NodeWriter extends AbstractWriter_1.default {
36
36
  });
37
37
  return [...files, ...directoryTemplateFiles];
38
38
  }
39
+ async writeAiDevelopmentFiles(destinationDir) {
40
+ const claude = await this.writeFileIfChangedMixinResults(spruce_skill_utils_1.diskUtil.resolvePath(destinationDir, 'CLAUDE.md'), this.templates.claudeMd(), 'Claude AI assistant configuration');
41
+ const agents = await this.writeFileIfChangedMixinResults(spruce_skill_utils_1.diskUtil.resolvePath(destinationDir, 'AGENTS.md'), this.templates.agentsMd(), 'AI agents documentation');
42
+ const project = await this.writeFileIfChangedMixinResults(spruce_skill_utils_1.diskUtil.resolvePath(destinationDir, 'docs', 'PROJECT.md'), this.templates.projectMd(), 'Project documentation for AI-assisted development');
43
+ return [...claude, ...agents, ...project];
44
+ }
39
45
  }
40
46
  exports.default = NodeWriter;
41
47
  //# sourceMappingURL=NodeWriter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeWriter.js","sourceRoot":"","sources":["../../../../src/features/node/writers/NodeWriter.ts"],"names":[],"mappings":";;;;;;AAAA,uEAAyD;AACzD,mEAAoE;AAEpE,qFAGwC;AAE3B,QAAA,qBAAqB,GAAG;IACjC,eAAe;IACf,mBAAmB;IACnB,YAAY;IACZ,QAAQ;CACX,CAAA;AACD,MAAqB,UAAW,SAAQ,wBAAc;IAC3C,KAAK,CAAC,eAAe,CACxB,cAAsB,EACtB,OAEC;QAED,IAAI,KAAK,GAAoB,EAAE,CAAA;QAC/B,IAAI,OAAO,EAAE,gBAAgB,KAAK,KAAK,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,qBAAqB,CAAA;YACtC,MAAM,WAAW,GAAG,6BAAQ,CAAC,WAAW,CACpC,cAAc,EACd,KAAK,EACL,UAAU,CACb,CAAA;YACD,6BAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YACzC,KAAK,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,cAAc;gBACpB,WAAW,EAAE,yBAAyB;gBACtC,MAAM,EAAE,WAAW;gBACnB,IAAI,EAAE,WAAW;aACpB,CAAC,CAAA;QACN,CAAC;QAED,MAAM,sBAAsB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC;YAC7D,cAAc;YACd,IAAI,EAAE,wCAAqB,CAAC,KAAK;YACjC,YAAY,EAAE,6BAAqB;YACnC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE;YACpD,GAAG,OAAO;SACb,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,sBAAsB,CAAC,CAAA;IAChD,CAAC;CACJ;AAlCD,6BAkCC"}
1
+ {"version":3,"file":"NodeWriter.js","sourceRoot":"","sources":["../../../../src/features/node/writers/NodeWriter.ts"],"names":[],"mappings":";;;;;;AAAA,uEAAyD;AACzD,mEAAoE;AAEpE,qFAGwC;AAE3B,QAAA,qBAAqB,GAAG;IACjC,eAAe;IACf,mBAAmB;IACnB,YAAY;IACZ,QAAQ;CACX,CAAA;AACD,MAAqB,UAAW,SAAQ,wBAAc;IAC3C,KAAK,CAAC,eAAe,CACxB,cAAsB,EACtB,OAEC;QAED,IAAI,KAAK,GAAoB,EAAE,CAAA;QAC/B,IAAI,OAAO,EAAE,gBAAgB,KAAK,KAAK,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,qBAAqB,CAAA;YACtC,MAAM,WAAW,GAAG,6BAAQ,CAAC,WAAW,CACpC,cAAc,EACd,KAAK,EACL,UAAU,CACb,CAAA;YACD,6BAAQ,CAAC,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YACzC,KAAK,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,cAAc;gBACpB,WAAW,EAAE,yBAAyB;gBACtC,MAAM,EAAE,WAAW;gBACnB,IAAI,EAAE,WAAW;aACpB,CAAC,CAAA;QACN,CAAC;QAED,MAAM,sBAAsB,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC;YAC7D,cAAc;YACd,IAAI,EAAE,wCAAqB,CAAC,KAAK;YACjC,YAAY,EAAE,6BAAqB;YACnC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE;YACpD,GAAG,OAAO;SACb,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,sBAAsB,CAAC,CAAA;IAChD,CAAC;IAEM,KAAK,CAAC,uBAAuB,CAAC,cAAsB;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,8BAA8B,CACpD,6BAAQ,CAAC,WAAW,CAAC,cAAc,EAAE,WAAW,CAAC,EACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EACzB,mCAAmC,CACtC,CAAA;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,8BAA8B,CACpD,6BAAQ,CAAC,WAAW,CAAC,cAAc,EAAE,WAAW,CAAC,EACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EACzB,yBAAyB,CAC5B,CAAA;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,8BAA8B,CACrD,6BAAQ,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,CAAC,EAC1D,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAC1B,mDAAmD,CACtD,CAAA;QAED,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,CAAA;IAC7C,CAAC;CACJ;AAxDD,6BAwDC"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "29.2.1",
7
+ "version": "29.3.0",
8
8
  "skill": {
9
9
  "namespace": "spruce-cli",
10
10
  "upgradeIgnoreList": [
@@ -85,7 +85,7 @@
85
85
  "@sprucelabs/spruce-core-schemas": "^41.3.41",
86
86
  "@sprucelabs/spruce-event-utils": "^41.0.160",
87
87
  "@sprucelabs/spruce-skill-utils": "^32.1.16",
88
- "@sprucelabs/spruce-templates": "^29.2.1",
88
+ "@sprucelabs/spruce-templates": "^29.3.0",
89
89
  "@typescript-eslint/eslint-plugin": "^8.46.2",
90
90
  "@typescript-eslint/parser": "^8.46.2",
91
91
  "cfonts": "^3.3.1",
@@ -570,5 +570,5 @@
570
570
  "conventional-changelog-sprucelabs"
571
571
  ]
572
572
  },
573
- "gitHead": "593e305372be973d416f4d81948b5c122041693a"
573
+ "gitHead": "9c87f912c4a6590ce3f67ca5306c3a0a65b0fc66"
574
574
  }
@@ -0,0 +1,79 @@
1
+ import { diskUtil } from '@sprucelabs/spruce-skill-utils'
2
+ import { test, assert } from '@sprucelabs/test-utils'
3
+ import AbstractSkillTest from '../../../tests/AbstractSkillTest'
4
+
5
+ export default class SettingUpForAiAssistedDevelopmentTest extends AbstractSkillTest {
6
+ protected static skillCacheKey = 'node'
7
+
8
+ private static readonly expectedFiles = [
9
+ 'CLAUDE.md',
10
+ 'AGENTS.md',
11
+ 'docs/PROJECT.md',
12
+ ]
13
+
14
+ @test()
15
+ protected static async hasSetupForAiDevelopmentAction() {
16
+ assert.isFunction(this.Action('node', 'setupForAiDevelopment').execute)
17
+ }
18
+
19
+ @test()
20
+ protected static async generatesExpectedFiles() {
21
+ await this.execute()
22
+ this.assertExpectedFilesExist()
23
+ }
24
+
25
+ @test()
26
+ protected static async generatedFilesHaveExpectedContents() {
27
+ this.assertFileContentsMatch('CLAUDE.md', 'markdown/CLAUDE.md.hbs')
28
+ this.assertFileContentsMatch('AGENTS.md', 'markdown/AGENTS.md.hbs')
29
+ this.assertFileContentsMatch(
30
+ 'docs/PROJECT.md',
31
+ 'markdown/PROJECT.md.hbs'
32
+ )
33
+ }
34
+
35
+ private static assertFileContentsMatch(
36
+ generatedFile: string,
37
+ templateFile: string
38
+ ) {
39
+ const generatedContents = diskUtil.readFile(
40
+ this.resolvePath(generatedFile)
41
+ )
42
+ const templateContents = this.readTemplate(templateFile)
43
+ assert.isEqual(generatedContents, templateContents)
44
+ }
45
+
46
+ private static readTemplate(templatePath: string) {
47
+ const fullPath = diskUtil.resolvePath(
48
+ __dirname,
49
+ '..',
50
+ '..',
51
+ '..',
52
+ '..',
53
+ '..',
54
+ 'spruce-templates',
55
+ 'src',
56
+ 'templates',
57
+ templatePath
58
+ )
59
+ return diskUtil.readFile(fullPath)
60
+ }
61
+
62
+ private static assertExpectedFilesExist() {
63
+ for (const file of this.expectedFiles) {
64
+ assert.isTrue(
65
+ diskUtil.doesFileExist(this.resolvePath(file)),
66
+ `Expected ${file} to exist`
67
+ )
68
+ }
69
+ }
70
+
71
+ private static async execute() {
72
+ const results = await this.Action(
73
+ 'node',
74
+ 'setupForAiDevelopment'
75
+ ).execute({})
76
+ assert.isFalsy(results.errors, 'Action should not return errors')
77
+ return results
78
+ }
79
+ }
@@ -0,0 +1,24 @@
1
+ import { buildSchema, SchemaValues } from '@sprucelabs/schema'
2
+ import AbstractAction from '../../AbstractAction'
3
+ import { FeatureActionResponse } from '../../features.types'
4
+
5
+ const optionsSchema = buildSchema({
6
+ id: 'setupForAiDevelopmentOptions',
7
+ fields: {},
8
+ })
9
+
10
+ type OptionsSchema = typeof optionsSchema
11
+ type Options = SchemaValues<OptionsSchema>
12
+
13
+ export default class SetupForAiDevelopmentAction extends AbstractAction<OptionsSchema> {
14
+ public optionsSchema = optionsSchema
15
+ public commandAliases = ['setup.ai']
16
+ public invocationMessage = 'Setting up for AI-assisted development... 🤖'
17
+
18
+ public async execute(_options: Options): Promise<FeatureActionResponse> {
19
+ const writer = this.Writer('node')
20
+ const files = await writer.writeAiDevelopmentFiles(this.cwd)
21
+
22
+ return { files }
23
+ }
24
+ }
@@ -46,4 +46,26 @@ export default class NodeWriter extends AbstractWriter {
46
46
 
47
47
  return [...files, ...directoryTemplateFiles]
48
48
  }
49
+
50
+ public async writeAiDevelopmentFiles(destinationDir: string) {
51
+ const claude = await this.writeFileIfChangedMixinResults(
52
+ diskUtil.resolvePath(destinationDir, 'CLAUDE.md'),
53
+ this.templates.claudeMd(),
54
+ 'Claude AI assistant configuration'
55
+ )
56
+
57
+ const agents = await this.writeFileIfChangedMixinResults(
58
+ diskUtil.resolvePath(destinationDir, 'AGENTS.md'),
59
+ this.templates.agentsMd(),
60
+ 'AI agents documentation'
61
+ )
62
+
63
+ const project = await this.writeFileIfChangedMixinResults(
64
+ diskUtil.resolvePath(destinationDir, 'docs', 'PROJECT.md'),
65
+ this.templates.projectMd(),
66
+ 'Project documentation for AI-assisted development'
67
+ )
68
+
69
+ return [...claude, ...agents, ...project]
70
+ }
49
71
  }