agile-context-engineering 0.2.2 → 0.5.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/.claude-plugin/plugin.json +10 -0
- package/CHANGELOG.md +82 -0
- package/README.md +27 -18
- package/agents/ace-product-owner.md +1 -1
- package/agents/ace-technical-application-architect.md +28 -0
- package/agents/ace-wiki-mapper.md +144 -29
- package/bin/install.js +67 -63
- package/hooks/ace-check-update.js +17 -9
- package/package.json +7 -5
- package/shared/lib/ace-core.js +308 -0
- package/shared/lib/ace-core.test.js +308 -0
- package/shared/lib/ace-github.js +753 -0
- package/shared/lib/ace-story.js +400 -0
- package/shared/lib/ace-story.test.js +250 -0
- package/{agile-context-engineering → shared}/utils/ui-formatting.md +299 -299
- package/skills/execute-story/SKILL.md +110 -0
- package/skills/execute-story/script.js +305 -0
- package/skills/execute-story/script.test.js +261 -0
- package/skills/execute-story/walkthrough-template.xml +255 -0
- package/{agile-context-engineering/workflows/execute-story.xml → skills/execute-story/workflow.xml} +83 -9
- package/skills/help/SKILL.md +69 -0
- package/skills/help/script.js +318 -0
- package/skills/help/script.test.js +183 -0
- package/{agile-context-engineering/workflows/help.xml → skills/help/workflow.xml} +8 -8
- package/skills/init-coding-standards/SKILL.md +72 -0
- package/{agile-context-engineering/templates/wiki/coding-standards.xml → skills/init-coding-standards/coding-standards-template.xml} +38 -0
- package/skills/init-coding-standards/script.js +59 -0
- package/skills/init-coding-standards/script.test.js +70 -0
- package/{agile-context-engineering/workflows/init-coding-standards.xml → skills/init-coding-standards/workflow.xml} +4 -9
- package/skills/map-cross-cutting/SKILL.md +89 -0
- package/skills/map-cross-cutting/workflow.xml +330 -0
- package/skills/map-guide/SKILL.md +89 -0
- package/skills/map-guide/workflow.xml +320 -0
- package/skills/map-pattern/SKILL.md +89 -0
- package/skills/map-pattern/workflow.xml +331 -0
- package/skills/map-story/SKILL.md +127 -0
- package/skills/map-story/templates/guide.xml +137 -0
- package/skills/map-story/templates/pattern.xml +159 -0
- package/skills/map-story/templates/system-cross-cutting.xml +197 -0
- package/skills/map-story/templates/walkthrough.xml +255 -0
- package/{agile-context-engineering/workflows/map-story.xml → skills/map-story/workflow.xml} +258 -9
- package/skills/map-subsystem/SKILL.md +111 -0
- package/skills/map-subsystem/script.js +60 -0
- package/skills/map-subsystem/script.test.js +68 -0
- package/skills/map-subsystem/templates/decizions.xml +115 -0
- package/skills/map-subsystem/templates/guide.xml +137 -0
- package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/module-discovery.xml +3 -3
- package/skills/map-subsystem/templates/pattern.xml +159 -0
- package/skills/map-subsystem/templates/system-cross-cutting.xml +197 -0
- package/skills/map-subsystem/templates/system.xml +381 -0
- package/skills/map-subsystem/templates/walkthrough.xml +255 -0
- package/{agile-context-engineering/workflows/map-subsystem.xml → skills/map-subsystem/workflow.xml} +17 -21
- package/skills/map-sys-doc/SKILL.md +90 -0
- package/skills/map-sys-doc/system.xml +381 -0
- package/skills/map-sys-doc/workflow.xml +336 -0
- package/skills/map-system/SKILL.md +85 -0
- package/skills/map-system/script.js +84 -0
- package/skills/map-system/script.test.js +73 -0
- package/skills/map-system/templates/wiki-readme.xml +297 -0
- package/{agile-context-engineering/workflows/map-system.xml → skills/map-system/workflow.xml} +11 -16
- package/skills/map-walkthrough/SKILL.md +92 -0
- package/skills/map-walkthrough/walkthrough.xml +255 -0
- package/skills/map-walkthrough/workflow.xml +457 -0
- package/skills/plan-backlog/SKILL.md +75 -0
- package/skills/plan-backlog/script.js +136 -0
- package/skills/plan-backlog/script.test.js +83 -0
- package/{agile-context-engineering/workflows/plan-backlog.xml → skills/plan-backlog/workflow.xml} +13 -21
- package/skills/plan-feature/SKILL.md +76 -0
- package/skills/plan-feature/script.js +148 -0
- package/skills/plan-feature/script.test.js +80 -0
- package/{agile-context-engineering/workflows/plan-feature.xml → skills/plan-feature/workflow.xml} +21 -29
- package/skills/plan-product-vision/SKILL.md +75 -0
- package/skills/plan-product-vision/script.js +60 -0
- package/skills/plan-product-vision/script.test.js +69 -0
- package/{agile-context-engineering/workflows/plan-product-vision.xml → skills/plan-product-vision/workflow.xml} +4 -9
- package/skills/plan-story/SKILL.md +116 -0
- package/skills/plan-story/script.js +326 -0
- package/skills/plan-story/script.test.js +240 -0
- package/skills/plan-story/story-template.xml +451 -0
- package/{agile-context-engineering/workflows/plan-story.xml → skills/plan-story/workflow.xml} +1285 -909
- package/skills/research-external-solution/SKILL.md +107 -0
- package/skills/research-external-solution/script.js +238 -0
- package/skills/research-external-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-external-solution.xml → skills/research-external-solution/workflow.xml} +4 -6
- package/skills/research-integration-solution/SKILL.md +98 -0
- package/{agile-context-engineering/templates/product/story-integration-solution.xml → skills/research-integration-solution/integration-solution-template.xml} +1 -0
- package/skills/research-integration-solution/script.js +231 -0
- package/skills/research-integration-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-integration-solution.xml → skills/research-integration-solution/workflow.xml} +4 -5
- package/skills/research-story-wiki/SKILL.md +92 -0
- package/skills/research-story-wiki/script.js +231 -0
- package/skills/research-story-wiki/script.test.js +138 -0
- package/{agile-context-engineering/templates/product/story-wiki.xml → skills/research-story-wiki/story-wiki-template.xml} +4 -0
- package/{agile-context-engineering/workflows/research-story-wiki.xml → skills/research-story-wiki/workflow.xml} +5 -6
- package/skills/research-technical-solution/SKILL.md +103 -0
- package/skills/research-technical-solution/script.js +231 -0
- package/skills/research-technical-solution/script.test.js +134 -0
- package/{agile-context-engineering/workflows/research-technical-solution.xml → skills/research-technical-solution/workflow.xml} +4 -5
- package/skills/review-story/SKILL.md +100 -0
- package/skills/review-story/script.js +257 -0
- package/skills/review-story/script.test.js +169 -0
- package/skills/review-story/story-template.xml +451 -0
- package/{agile-context-engineering/workflows/review-story.xml → skills/review-story/workflow.xml} +1 -3
- package/skills/update/SKILL.md +53 -0
- package/{agile-context-engineering/workflows/update.xml → skills/update/workflow.xml} +237 -207
- package/agile-context-engineering/src/ace-tools.js +0 -2881
- package/agile-context-engineering/src/ace-tools.test.js +0 -1089
- package/agile-context-engineering/templates/_command.md +0 -54
- package/agile-context-engineering/templates/_workflow.xml +0 -17
- package/agile-context-engineering/templates/config.json +0 -0
- package/agile-context-engineering/templates/product/integration-solution.xml +0 -0
- package/agile-context-engineering/templates/wiki/wiki-readme.xml +0 -276
- package/commands/ace/execute-story.md +0 -137
- package/commands/ace/help.md +0 -93
- package/commands/ace/init-coding-standards.md +0 -83
- package/commands/ace/map-story.md +0 -156
- package/commands/ace/map-subsystem.md +0 -138
- package/commands/ace/map-system.md +0 -92
- package/commands/ace/plan-backlog.md +0 -83
- package/commands/ace/plan-feature.md +0 -89
- package/commands/ace/plan-product-vision.md +0 -81
- package/commands/ace/plan-story.md +0 -145
- package/commands/ace/research-external-solution.md +0 -138
- package/commands/ace/research-integration-solution.md +0 -135
- package/commands/ace/research-story-wiki.md +0 -116
- package/commands/ace/research-technical-solution.md +0 -147
- package/commands/ace/review-story.md +0 -109
- package/commands/ace/update.md +0 -54
- /package/{agile-context-engineering → shared}/utils/questioning.xml +0 -0
- /package/{agile-context-engineering/templates/product/story.xml → skills/execute-story/story-template.xml} +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-cross-cutting}/system-cross-cutting.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-guide}/guide.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-pattern}/pattern.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/decizions.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/system.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-story/templates}/tech-debt-index.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-architecture.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-subsystem/templates}/subsystem-structure.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-architecture.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/system-structure.xml +0 -0
- /package/{agile-context-engineering/templates/wiki → skills/map-system/templates}/testing-framework.xml +0 -0
- /package/{agile-context-engineering/templates/product/product-backlog.xml → skills/plan-backlog/product-backlog-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/feature.xml → skills/plan-feature/feature-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/product-vision.xml → skills/plan-product-vision/product-vision-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/external-solution.xml → skills/research-external-solution/external-solution-template.xml} +0 -0
- /package/{agile-context-engineering/templates/product/story-technical-solution.xml → skills/research-technical-solution/technical-solution-template.xml} +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* help skill script — Entry point for all ace-tools operations
|
|
5
|
+
* needed by the help skill.
|
|
6
|
+
*
|
|
7
|
+
* Subcommands:
|
|
8
|
+
* init Environment detection for help workflow
|
|
9
|
+
* ensure-settings Create .ace/settings.json with defaults if missing
|
|
10
|
+
* setup-github Detect gh CLI, repo, and list GitHub Projects
|
|
11
|
+
* write-github-settings Write GitHub Project settings (key=value args)
|
|
12
|
+
* sync-agent-teams Sync agent_teams from runtime settings to .ace/settings.json
|
|
13
|
+
* write-agent-teams <true|false> Enable/disable agent teams in ACE + runtime settings
|
|
14
|
+
* verify-path-exists <path> Check file/directory existence
|
|
15
|
+
*
|
|
16
|
+
* Usage: node script.js <subcommand> [args] [--raw]
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
loadConfig, pathExists, resolveModel,
|
|
24
|
+
detectBrownfieldStatus, loadSettings, writeSettings,
|
|
25
|
+
output, error,
|
|
26
|
+
} = require('../../shared/lib/ace-core');
|
|
27
|
+
|
|
28
|
+
// ─── Runtime Config Dir ─────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Detect the runtime config directory name.
|
|
32
|
+
* In the plugin context, the script lives at:
|
|
33
|
+
* <base>/<config-dir>/skills/help/script.js
|
|
34
|
+
* Default to '.claude' since the plugin always runs in Claude Code.
|
|
35
|
+
*/
|
|
36
|
+
function getRuntimeConfigDirName() {
|
|
37
|
+
try {
|
|
38
|
+
const skillDir = __dirname; // <base>/<config-dir>/skills/help
|
|
39
|
+
const skillsDir = path.dirname(skillDir); // <base>/<config-dir>/skills
|
|
40
|
+
const configDir = path.dirname(skillsDir); // <base>/<config-dir>
|
|
41
|
+
const dirName = path.basename(configDir);
|
|
42
|
+
if (dirName === '.opencode' || dirName === '.claude') {
|
|
43
|
+
return dirName;
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
return '.claude';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const RUNTIME_CONFIG_DIR = getRuntimeConfigDirName();
|
|
50
|
+
|
|
51
|
+
// ─── CLI Dispatch ────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
const cwd = process.cwd();
|
|
54
|
+
const args = process.argv.slice(2);
|
|
55
|
+
const raw = args.includes('--raw');
|
|
56
|
+
const cmd = args[0];
|
|
57
|
+
|
|
58
|
+
switch (cmd) {
|
|
59
|
+
case 'init':
|
|
60
|
+
cmdInit(cwd, raw);
|
|
61
|
+
break;
|
|
62
|
+
case 'ensure-settings':
|
|
63
|
+
cmdEnsureSettings(cwd, raw);
|
|
64
|
+
break;
|
|
65
|
+
case 'setup-github':
|
|
66
|
+
cmdSetupGithubProject(cwd, raw);
|
|
67
|
+
break;
|
|
68
|
+
case 'write-github-settings':
|
|
69
|
+
cmdWriteGithubSettings(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
|
|
70
|
+
break;
|
|
71
|
+
case 'sync-agent-teams':
|
|
72
|
+
cmdSyncAgentTeams(cwd, raw);
|
|
73
|
+
break;
|
|
74
|
+
case 'write-agent-teams':
|
|
75
|
+
cmdWriteAgentTeamsSetting(cwd, raw, args.slice(1).filter(a => a !== '--raw'));
|
|
76
|
+
break;
|
|
77
|
+
case 'verify-path-exists': {
|
|
78
|
+
const targetPath = args[1];
|
|
79
|
+
if (!targetPath) error('path required for verify-path-exists');
|
|
80
|
+
const exists = pathExists(cwd, targetPath);
|
|
81
|
+
output({ exists, path: targetPath }, raw, exists ? 'true' : 'false');
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
error(`Unknown command: ${cmd}\nAvailable: init, ensure-settings, setup-github, write-github-settings, sync-agent-teams, write-agent-teams, verify-path-exists`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ─── Init: Help ─────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Environment detection for the help workflow (project status dashboard).
|
|
92
|
+
* Detects: git, gh CLI, brownfield status, wiki state, product artifacts.
|
|
93
|
+
*/
|
|
94
|
+
function cmdInit(cwd, raw) {
|
|
95
|
+
const config = loadConfig(cwd);
|
|
96
|
+
const brownfield = detectBrownfieldStatus(cwd);
|
|
97
|
+
|
|
98
|
+
const result = {
|
|
99
|
+
// Models (pre-resolved so workflows know which model to spawn each agent with)
|
|
100
|
+
product_owner_model: resolveModel(cwd, 'ace-product-owner'),
|
|
101
|
+
researcher_model: resolveModel(cwd, 'ace-project-researcher'),
|
|
102
|
+
synthesizer_model: resolveModel(cwd, 'ace-research-synthesizer'),
|
|
103
|
+
|
|
104
|
+
// Config
|
|
105
|
+
commit_docs: config.commit_docs,
|
|
106
|
+
|
|
107
|
+
// Existing state
|
|
108
|
+
has_product_vision: pathExists(cwd, '.docs/product/product-vision.md'),
|
|
109
|
+
has_system_architecture: pathExists(cwd, '.docs/wiki/system-wide/system-architecture.md'),
|
|
110
|
+
has_system_structure: pathExists(cwd, '.docs/wiki/system-wide/system-structure.md'),
|
|
111
|
+
has_coding_standards: pathExists(cwd, '.docs/wiki/system-wide/coding-standards.md'),
|
|
112
|
+
has_testing_framework: pathExists(cwd, '.docs/wiki/system-wide/testing-framework.md'),
|
|
113
|
+
project_exists: pathExists(cwd, '.docs/product/product-vision.md'),
|
|
114
|
+
has_codebase_map: pathExists(cwd, '.ace/codebase'),
|
|
115
|
+
planning_exists: pathExists(cwd, '.ace'),
|
|
116
|
+
|
|
117
|
+
// Brownfield detection
|
|
118
|
+
...brownfield,
|
|
119
|
+
needs_codebase_map: brownfield.is_brownfield && !pathExists(cwd, '.ace/codebase'),
|
|
120
|
+
|
|
121
|
+
// Git state
|
|
122
|
+
has_git: pathExists(cwd, '.git'),
|
|
123
|
+
|
|
124
|
+
// GitHub CLI
|
|
125
|
+
has_gh_cli: (() => {
|
|
126
|
+
try {
|
|
127
|
+
const { execSync } = require('child_process');
|
|
128
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
129
|
+
return true;
|
|
130
|
+
} catch { return false; }
|
|
131
|
+
})(),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
output(result, raw);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── Ensure Settings ────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
function cmdEnsureSettings(cwd, raw) {
|
|
140
|
+
const settingsPath = path.join(cwd, '.ace', 'settings.json');
|
|
141
|
+
const alreadyExists = pathExists(cwd, '.ace/settings.json');
|
|
142
|
+
|
|
143
|
+
if (!alreadyExists) {
|
|
144
|
+
const SETTINGS_DEFAULTS = {
|
|
145
|
+
model_profile: 'balanced',
|
|
146
|
+
commit_docs: true,
|
|
147
|
+
agent_teams: false,
|
|
148
|
+
github_project: {
|
|
149
|
+
enabled: false,
|
|
150
|
+
gh_installed: false,
|
|
151
|
+
repo: '',
|
|
152
|
+
project_number: null,
|
|
153
|
+
owner: '',
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
const defaults = JSON.parse(JSON.stringify(SETTINGS_DEFAULTS));
|
|
157
|
+
writeSettings(cwd, defaults);
|
|
158
|
+
output({ created: true, path: settingsPath, settings: defaults }, raw);
|
|
159
|
+
} else {
|
|
160
|
+
const settings = loadSettings(cwd);
|
|
161
|
+
output({ created: false, path: settingsPath, settings }, raw);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ─── Setup GitHub Project ───────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
function cmdSetupGithubProject(cwd, raw) {
|
|
168
|
+
const { execSync } = require('child_process');
|
|
169
|
+
const settings = loadSettings(cwd);
|
|
170
|
+
|
|
171
|
+
// Detect gh CLI
|
|
172
|
+
let ghInstalled = false;
|
|
173
|
+
try {
|
|
174
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
175
|
+
ghInstalled = true;
|
|
176
|
+
} catch {}
|
|
177
|
+
|
|
178
|
+
// Detect repo
|
|
179
|
+
let repo = '';
|
|
180
|
+
let owner = '';
|
|
181
|
+
if (ghInstalled) {
|
|
182
|
+
try {
|
|
183
|
+
repo = execSync('gh repo view --json nameWithOwner -q .nameWithOwner', {
|
|
184
|
+
cwd,
|
|
185
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
186
|
+
encoding: 'utf-8',
|
|
187
|
+
}).trim();
|
|
188
|
+
owner = repo.split('/')[0] || '';
|
|
189
|
+
} catch {}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// List projects
|
|
193
|
+
let projects = [];
|
|
194
|
+
if (ghInstalled && owner) {
|
|
195
|
+
try {
|
|
196
|
+
const projectsJson = execSync(`gh project list --owner ${owner} --limit 10 --format json`, {
|
|
197
|
+
cwd,
|
|
198
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
199
|
+
encoding: 'utf-8',
|
|
200
|
+
}).trim();
|
|
201
|
+
const parsed = JSON.parse(projectsJson);
|
|
202
|
+
projects = (parsed.projects || parsed || []).map(p => ({
|
|
203
|
+
number: p.number,
|
|
204
|
+
title: p.title,
|
|
205
|
+
}));
|
|
206
|
+
} catch {}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
output({
|
|
210
|
+
gh_installed: ghInstalled,
|
|
211
|
+
repo,
|
|
212
|
+
owner,
|
|
213
|
+
projects,
|
|
214
|
+
current_settings: settings.github_project,
|
|
215
|
+
}, raw);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── Write GitHub Settings ──────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
function cmdWriteGithubSettings(cwd, raw, extraArgs) {
|
|
221
|
+
const settings = loadSettings(cwd);
|
|
222
|
+
|
|
223
|
+
for (const arg of extraArgs) {
|
|
224
|
+
const eqIndex = arg.indexOf('=');
|
|
225
|
+
if (eqIndex === -1) continue;
|
|
226
|
+
const key = arg.substring(0, eqIndex);
|
|
227
|
+
const value = arg.substring(eqIndex + 1);
|
|
228
|
+
|
|
229
|
+
switch (key) {
|
|
230
|
+
case 'enabled':
|
|
231
|
+
settings.github_project.enabled = value === 'true';
|
|
232
|
+
break;
|
|
233
|
+
case 'gh_installed':
|
|
234
|
+
settings.github_project.gh_installed = value === 'true';
|
|
235
|
+
break;
|
|
236
|
+
case 'repo':
|
|
237
|
+
settings.github_project.repo = value;
|
|
238
|
+
break;
|
|
239
|
+
case 'project_number':
|
|
240
|
+
settings.github_project.project_number = value === 'null' ? null : parseInt(value, 10);
|
|
241
|
+
break;
|
|
242
|
+
case 'owner':
|
|
243
|
+
settings.github_project.owner = value;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
writeSettings(cwd, settings);
|
|
249
|
+
output({ written: true, settings }, raw);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Sync Agent Teams ───────────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
function cmdSyncAgentTeams(cwd, raw) {
|
|
255
|
+
// Source of truth: runtime settings.json env var (e.g. .claude/settings.json)
|
|
256
|
+
const claudeSettingsPath = path.join(cwd, RUNTIME_CONFIG_DIR, 'settings.json');
|
|
257
|
+
let claudeEnabled = false;
|
|
258
|
+
try {
|
|
259
|
+
const claudeRaw = fs.readFileSync(claudeSettingsPath, 'utf-8');
|
|
260
|
+
const claudeSettings = JSON.parse(claudeRaw);
|
|
261
|
+
const val = claudeSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
262
|
+
claudeEnabled = val === '1' || val === 'true';
|
|
263
|
+
} catch {
|
|
264
|
+
// File doesn't exist or is invalid — treat as disabled
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Sync ACE settings to match Claude's source of truth
|
|
268
|
+
const settings = loadSettings(cwd);
|
|
269
|
+
const wasDifferent = settings.agent_teams !== claudeEnabled;
|
|
270
|
+
if (wasDifferent) {
|
|
271
|
+
settings.agent_teams = claudeEnabled;
|
|
272
|
+
writeSettings(cwd, settings);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
output({ agent_teams: claudeEnabled, synced: wasDifferent }, raw);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ─── Write Agent Teams Setting ──────────────────────────────────────────────
|
|
279
|
+
|
|
280
|
+
function cmdWriteAgentTeamsSetting(cwd, raw, extraArgs) {
|
|
281
|
+
const enabled = extraArgs[0] === 'true';
|
|
282
|
+
const settings = loadSettings(cwd);
|
|
283
|
+
settings.agent_teams = enabled;
|
|
284
|
+
writeSettings(cwd, settings);
|
|
285
|
+
|
|
286
|
+
// Also update the project's runtime settings.json (e.g. .claude/)
|
|
287
|
+
const claudeDir = path.join(cwd, RUNTIME_CONFIG_DIR);
|
|
288
|
+
const claudeSettingsPath = path.join(claudeDir, 'settings.json');
|
|
289
|
+
|
|
290
|
+
let claudeSettings = {};
|
|
291
|
+
try {
|
|
292
|
+
const existing = fs.readFileSync(claudeSettingsPath, 'utf-8');
|
|
293
|
+
claudeSettings = JSON.parse(existing);
|
|
294
|
+
} catch {
|
|
295
|
+
// File doesn't exist or is invalid — start fresh
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!claudeSettings.env) {
|
|
299
|
+
claudeSettings.env = {};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (enabled) {
|
|
303
|
+
claudeSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';
|
|
304
|
+
} else {
|
|
305
|
+
delete claudeSettings.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
306
|
+
// Clean up empty env object
|
|
307
|
+
if (Object.keys(claudeSettings.env).length === 0) {
|
|
308
|
+
delete claudeSettings.env;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!fs.existsSync(claudeDir)) {
|
|
313
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
314
|
+
}
|
|
315
|
+
fs.writeFileSync(claudeSettingsPath, JSON.stringify(claudeSettings, null, 2) + '\n', 'utf-8');
|
|
316
|
+
|
|
317
|
+
output({ written: true, agent_teams: enabled, settings, claude_settings: claudeSettings }, raw);
|
|
318
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const { describe, it, before, after } = require('node:test');
|
|
2
|
+
const assert = require('node:assert');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const SCRIPT = path.join(__dirname, 'script.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a minimal ACE project structure in a temp directory.
|
|
12
|
+
*/
|
|
13
|
+
function createTestProject() {
|
|
14
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
|
|
15
|
+
|
|
16
|
+
// .ace/config.json
|
|
17
|
+
const aceDir = path.join(tmpDir, '.ace');
|
|
18
|
+
fs.mkdirSync(aceDir, { recursive: true });
|
|
19
|
+
fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
|
|
20
|
+
version: '0.1.0',
|
|
21
|
+
projectName: 'test-project',
|
|
22
|
+
model_profile: 'quality',
|
|
23
|
+
commit_docs: true,
|
|
24
|
+
github: { enabled: false },
|
|
25
|
+
}, null, 2));
|
|
26
|
+
|
|
27
|
+
// .ace/settings.json
|
|
28
|
+
fs.writeFileSync(path.join(aceDir, 'settings.json'), JSON.stringify({
|
|
29
|
+
model_profile: 'quality',
|
|
30
|
+
commit_docs: true,
|
|
31
|
+
agent_teams: false,
|
|
32
|
+
github_project: { enabled: false, gh_installed: false, repo: '', project_number: null, owner: '' },
|
|
33
|
+
}, null, 2));
|
|
34
|
+
|
|
35
|
+
return tmpDir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function runScript(subcommand, args, cwd) {
|
|
39
|
+
return execSync(`node "${SCRIPT}" ${subcommand} ${args}`, {
|
|
40
|
+
cwd,
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
timeout: 10000,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function cleanup(tmpDir) {
|
|
47
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
describe('help script', () => {
|
|
53
|
+
|
|
54
|
+
describe('init', () => {
|
|
55
|
+
let tmpDir;
|
|
56
|
+
|
|
57
|
+
before(() => { tmpDir = createTestProject(); });
|
|
58
|
+
after(() => { cleanup(tmpDir); });
|
|
59
|
+
|
|
60
|
+
it('returns valid JSON with environment detection', () => {
|
|
61
|
+
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
62
|
+
|
|
63
|
+
assert.strictEqual(typeof result.commit_docs, 'boolean');
|
|
64
|
+
assert.strictEqual(typeof result.has_git, 'boolean');
|
|
65
|
+
assert.strictEqual(typeof result.has_product_vision, 'boolean');
|
|
66
|
+
assert.strictEqual(typeof result.has_system_architecture, 'boolean');
|
|
67
|
+
assert.strictEqual(typeof result.has_system_structure, 'boolean');
|
|
68
|
+
assert.strictEqual(typeof result.has_coding_standards, 'boolean');
|
|
69
|
+
assert.strictEqual(typeof result.has_testing_framework, 'boolean');
|
|
70
|
+
assert.strictEqual(typeof result.is_brownfield, 'boolean');
|
|
71
|
+
assert.strictEqual(typeof result.is_greenfield, 'boolean');
|
|
72
|
+
assert.ok(result.product_owner_model, 'should have product_owner_model');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('detects brownfield vs greenfield correctly', () => {
|
|
76
|
+
const result = JSON.parse(runScript('init', '', tmpDir));
|
|
77
|
+
assert.strictEqual(result.is_brownfield, !result.is_greenfield);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('ensure-settings', () => {
|
|
82
|
+
let tmpDir;
|
|
83
|
+
|
|
84
|
+
before(() => { tmpDir = createTestProject(); });
|
|
85
|
+
after(() => { cleanup(tmpDir); });
|
|
86
|
+
|
|
87
|
+
it('reports settings already exist when they do', () => {
|
|
88
|
+
const result = JSON.parse(runScript('ensure-settings', '', tmpDir));
|
|
89
|
+
assert.strictEqual(result.created, false);
|
|
90
|
+
assert.ok(result.settings);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('creates settings when they do not exist', () => {
|
|
94
|
+
const freshDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ace-test-'));
|
|
95
|
+
const aceDir = path.join(freshDir, '.ace');
|
|
96
|
+
fs.mkdirSync(aceDir, { recursive: true });
|
|
97
|
+
fs.writeFileSync(path.join(aceDir, 'config.json'), JSON.stringify({
|
|
98
|
+
version: '0.1.0',
|
|
99
|
+
projectName: 'test',
|
|
100
|
+
model_profile: 'balanced',
|
|
101
|
+
commit_docs: true,
|
|
102
|
+
github: { enabled: false },
|
|
103
|
+
}, null, 2));
|
|
104
|
+
|
|
105
|
+
const result = JSON.parse(runScript('ensure-settings', '', freshDir));
|
|
106
|
+
assert.strictEqual(result.created, true);
|
|
107
|
+
assert.ok(result.settings);
|
|
108
|
+
assert.strictEqual(result.settings.model_profile, 'balanced');
|
|
109
|
+
|
|
110
|
+
cleanup(freshDir);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('sync-agent-teams', () => {
|
|
115
|
+
let tmpDir;
|
|
116
|
+
|
|
117
|
+
before(() => { tmpDir = createTestProject(); });
|
|
118
|
+
after(() => { cleanup(tmpDir); });
|
|
119
|
+
|
|
120
|
+
it('returns agent_teams boolean', () => {
|
|
121
|
+
const result = JSON.parse(runScript('sync-agent-teams', '', tmpDir));
|
|
122
|
+
assert.strictEqual(typeof result.agent_teams, 'boolean');
|
|
123
|
+
assert.strictEqual(typeof result.synced, 'boolean');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('write-agent-teams', () => {
|
|
128
|
+
let tmpDir;
|
|
129
|
+
|
|
130
|
+
before(() => { tmpDir = createTestProject(); });
|
|
131
|
+
after(() => { cleanup(tmpDir); });
|
|
132
|
+
|
|
133
|
+
it('enables agent teams', () => {
|
|
134
|
+
const result = JSON.parse(runScript('write-agent-teams', 'true', tmpDir));
|
|
135
|
+
assert.strictEqual(result.written, true);
|
|
136
|
+
assert.strictEqual(result.agent_teams, true);
|
|
137
|
+
|
|
138
|
+
// Verify .ace/settings.json updated
|
|
139
|
+
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
140
|
+
assert.strictEqual(settings.agent_teams, true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('disables agent teams', () => {
|
|
144
|
+
const result = JSON.parse(runScript('write-agent-teams', 'false', tmpDir));
|
|
145
|
+
assert.strictEqual(result.written, true);
|
|
146
|
+
assert.strictEqual(result.agent_teams, false);
|
|
147
|
+
|
|
148
|
+
const settings = JSON.parse(fs.readFileSync(path.join(tmpDir, '.ace', 'settings.json'), 'utf-8'));
|
|
149
|
+
assert.strictEqual(settings.agent_teams, false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('verify-path-exists', () => {
|
|
154
|
+
let tmpDir;
|
|
155
|
+
|
|
156
|
+
before(() => { tmpDir = createTestProject(); });
|
|
157
|
+
after(() => { cleanup(tmpDir); });
|
|
158
|
+
|
|
159
|
+
it('returns true for existing path', () => {
|
|
160
|
+
const result = runScript('verify-path-exists', '.ace/config.json --raw', tmpDir).trim();
|
|
161
|
+
assert.strictEqual(result, 'true');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('returns false for non-existing path', () => {
|
|
165
|
+
const result = runScript('verify-path-exists', 'nonexistent/file.md --raw', tmpDir).trim();
|
|
166
|
+
assert.strictEqual(result, 'false');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('error handling', () => {
|
|
171
|
+
it('errors on unknown command', () => {
|
|
172
|
+
assert.throws(() => {
|
|
173
|
+
execSync(`node "${SCRIPT}" bogus`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('errors on verify-path-exists without path', () => {
|
|
178
|
+
assert.throws(() => {
|
|
179
|
+
execSync(`node "${SCRIPT}" verify-path-exists`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<substep order="1.1" name="environment-detection">
|
|
24
24
|
```bash
|
|
25
|
-
INIT=$(node
|
|
25
|
+
INIT=$(node "${CLAUDE_SKILL_DIR}/script.js" init)
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
<output-fields>
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
<substep order="2.1" name="create-settings-file">
|
|
62
62
|
```bash
|
|
63
|
-
SETTINGS=$(node
|
|
63
|
+
SETTINGS=$(node "${CLAUDE_SKILL_DIR}/script.js" ensure-settings)
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
<output-fields>
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
|
|
97
97
|
<substep order="3.1" name="github-detection">
|
|
98
98
|
```bash
|
|
99
|
-
GH_STATUS=$(node
|
|
99
|
+
GH_STATUS=$(node "${CLAUDE_SKILL_DIR}/script.js" setup-github)
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
<output-fields>
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
|
|
219
219
|
<substep order="3.7" name="write-github-settings">
|
|
220
220
|
```bash
|
|
221
|
-
node
|
|
221
|
+
node "${CLAUDE_SKILL_DIR}/script.js" write-github-settings enabled=true gh_installed=true repo={repo} project_number={number} owner={owner}
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
Display:
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
**MANDATORY — Run this command before evaluating state:**
|
|
246
246
|
|
|
247
247
|
```bash
|
|
248
|
-
AGENT_TEAMS=$(node
|
|
248
|
+
AGENT_TEAMS=$(node "${CLAUDE_SKILL_DIR}/script.js" sync-agent-teams --raw)
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
Parse the JSON output. The `agent_teams` field is the synced boolean state.
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
|
|
268
268
|
<variant condition="user chose Disable">
|
|
269
269
|
```bash
|
|
270
|
-
node
|
|
270
|
+
node "${CLAUDE_SKILL_DIR}/script.js" write-agent-teams false
|
|
271
271
|
```
|
|
272
272
|
Display:
|
|
273
273
|
```
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
|
|
287
287
|
<variant condition="user chose Enable Agent Teams">
|
|
288
288
|
```bash
|
|
289
|
-
node
|
|
289
|
+
node "${CLAUDE_SKILL_DIR}/script.js" write-agent-teams true
|
|
290
290
|
```
|
|
291
291
|
Display:
|
|
292
292
|
```
|
|
@@ -455,7 +455,7 @@
|
|
|
455
455
|
|
|
456
456
|
<substep order="9.1" name="check-backlog-exists">
|
|
457
457
|
```bash
|
|
458
|
-
HAS_BACKLOG=$(node
|
|
458
|
+
HAS_BACKLOG=$(node "${CLAUDE_SKILL_DIR}/script.js" verify-path-exists .ace/artifacts/product/product-backlog.md --raw)
|
|
459
459
|
```
|
|
460
460
|
|
|
461
461
|
<variant condition="HAS_BACKLOG is true">
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: init-coding-standards
|
|
3
|
+
description: Generate a tailored coding-standards.md through codebase detection and user interview
|
|
4
|
+
argument-hint: "[optional: context='existing standards doc or notes to build on']"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools: Read, Bash, Glob, Grep, Write, Task, AskUserQuestion
|
|
7
|
+
model: sonnet
|
|
8
|
+
effort: high
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Init Coding Standards
|
|
12
|
+
|
|
13
|
+
Generate a tailored coding-standards.md through codebase detection and user interview.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- During `/ace:help` — as part of initial project setup
|
|
18
|
+
- After `/ace:map-system` — once codebase is mapped, add prescriptive standards
|
|
19
|
+
- Anytime — to create or refresh coding standards for a project
|
|
20
|
+
- Starting a new project and want to establish coding standards upfront (greenfield or brownfield)
|
|
21
|
+
- Onboarding AI agents to an existing codebase (prevents common AI mistakes)
|
|
22
|
+
- Current coding standards are outdated or missing
|
|
23
|
+
- Team has pain points with AI-generated code quality
|
|
24
|
+
|
|
25
|
+
## Input
|
|
26
|
+
|
|
27
|
+
### Optional
|
|
28
|
+
|
|
29
|
+
- **`context`** — Existing coding standards document, style guide, or notes to use as a starting point. Will be refined through the interview process.
|
|
30
|
+
|
|
31
|
+
## Environment Context (preprocessed)
|
|
32
|
+
|
|
33
|
+
!`node "${CLAUDE_SKILL_DIR}/script.js" init "$ARGUMENTS" 2>/dev/null`
|
|
34
|
+
|
|
35
|
+
## Supporting Resources
|
|
36
|
+
|
|
37
|
+
Read ALL of these before starting the workflow:
|
|
38
|
+
|
|
39
|
+
- **Workflow**: Read [workflow.xml](workflow.xml) — complete orchestration process with all steps
|
|
40
|
+
- **Coding standards template**: Read [coding-standards-template.xml](coding-standards-template.xml) — output format for the coding standards
|
|
41
|
+
- **Questioning guide**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/questioning.xml` — deep questioning techniques
|
|
42
|
+
- **UI formatting**: Read `${CLAUDE_SKILL_DIR}/../../shared/utils/ui-formatting.md` — ACE output formatting rules
|
|
43
|
+
|
|
44
|
+
## Process
|
|
45
|
+
|
|
46
|
+
Use the `ace-product-owner` agent for interviewing the user about coding philosophy and pain points.
|
|
47
|
+
|
|
48
|
+
The Environment Context above contains the preprocessed INIT JSON — use it directly instead of running the init script manually. The workflow's step 1 setup can skip the init bash call since that data is already available.
|
|
49
|
+
|
|
50
|
+
Read all supporting resources listed above, then execute the workflow defined in [workflow.xml](workflow.xml) end-to-end. Preserve all workflow gates (validation, approvals, commits).
|
|
51
|
+
|
|
52
|
+
## Artifacts
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
.docs/wiki/system-wide/coding-standards.md
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Example Usage
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
# Generate coding standards from codebase analysis + interview
|
|
62
|
+
/ace:init-coding-standards
|
|
63
|
+
|
|
64
|
+
# Generate with existing standards as seed
|
|
65
|
+
/ace:init-coding-standards context="path/to/existing-style-guide.md"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Next Steps
|
|
69
|
+
|
|
70
|
+
- `/ace:map-system` — Map codebase structure and architecture
|
|
71
|
+
- `/ace:help` — Check project initialization status and next steps
|
|
72
|
+
- Review and edit `.docs/wiki/system-wide/coding-standards.md` anytime
|
|
@@ -60,6 +60,44 @@
|
|
|
60
60
|
- **NEVER leave stub implementations** — No `throw NotImplemented`, no `pass`, no `...`
|
|
61
61
|
- If code exists in the codebase, it must be complete and functional
|
|
62
62
|
|
|
63
|
+
### Defensive Programming — Zero Tolerance for Permissive Code
|
|
64
|
+
|
|
65
|
+
**WE USE DEFENSIVE PROGRAMMING + FAIL-FAST. PERMISSIVE PROGRAMMING IS BANNED.**
|
|
66
|
+
|
|
67
|
+
Permissive programming ("be liberal in what you accept") has caused catastrophic bugs.
|
|
68
|
+
It hides errors, delays failures, and makes debugging impossible. Every parameter must
|
|
69
|
+
be validated. Every error must be surfaced. No exceptions.
|
|
70
|
+
|
|
71
|
+
**MANDATORY — Do This:**
|
|
72
|
+
- **Validate EVERY input at the boundary** — check types, ranges, formats, required
|
|
73
|
+
fields BEFORE processing
|
|
74
|
+
- **Fail fast and loud** — if something is wrong, return an error immediately with a
|
|
75
|
+
clear message explaining WHAT is wrong and WHY
|
|
76
|
+
- **Read the function you're calling** — check its constructor/signature to know EXACTLY
|
|
77
|
+
what parameters it requires before writing the caller
|
|
78
|
+
- **Required means required** — if a function needs a value, the caller MUST provide it.
|
|
79
|
+
No nullable wrappers. No default fallbacks that mask missing data
|
|
80
|
+
- **Return errors, not defaults** — if a value is missing or invalid, return an error
|
|
81
|
+
string/throw, do NOT return `""`, `0`, `null`, `[]`, or any placeholder
|
|
82
|
+
- **Validate on BOTH client and server** — server validates before processing/pushing,
|
|
83
|
+
client validates as a redundant safety net. Both layers reject garbage
|
|
84
|
+
- **Surface errors visibly** — errors must reach whoever can fix them (the LLM, the user,
|
|
85
|
+
the developer). Log them, display them, return them. Never swallow them
|
|
86
|
+
|
|
87
|
+
**ABSOLUTELY FORBIDDEN — Never Do This:**
|
|
88
|
+
- `string? param = null` when the value is actually required — use `string param` and validate
|
|
89
|
+
- `return ""` or `return null` or `return []` when an operation fails — return an error with context
|
|
90
|
+
- `.optional()` or `.nullable()` on schema fields that the consuming function REQUIRES
|
|
91
|
+
- Fallback defaults that hide missing data (e.g., `value ?? defaultValue` to mask a null
|
|
92
|
+
that should never be null)
|
|
93
|
+
- `try/catch` that swallows exceptions and returns empty objects
|
|
94
|
+
- Silently stripping, transforming, or cleaning invalid data to make it pass validation
|
|
95
|
+
- Writing a caller without reading the callee's actual parameter signature first
|
|
96
|
+
- Using Postel's Law ("be liberal in what you accept") as justification for accepting garbage
|
|
97
|
+
|
|
98
|
+
**The Principle:**
|
|
99
|
+
**Garbage in → ERROR out. Never garbage in → silence.**
|
|
100
|
+
|
|
63
101
|
### No Assumptions
|
|
64
102
|
- **Use LSPs first when available** — Check diagnostics, go-to-definition, and find-references before guessing. The LSP knows the codebase better than a text search.
|
|
65
103
|
- **NEVER assume** libraries, classes, models, methods, or APIs exist
|