niuma-harness 0.0.1
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 +21 -0
- package/README.md +105 -0
- package/bin/niuma-harness.js +8 -0
- package/package.json +42 -0
- package/src/args.js +176 -0
- package/src/cli.js +23 -0
- package/src/prompts.js +61 -0
- package/src/scaffold.js +207 -0
- package/templates/core/HARNESS_GUIDE.md +35 -0
- package/templates/core/docs/automation/hooks.md +16 -0
- package/templates/core/docs/index.md +38 -0
- package/templates/core/docs/process/bugfix.md +19 -0
- package/templates/core/docs/process/feature-development.md +29 -0
- package/templates/core/docs/process/task-triage.md +29 -0
- package/templates/core/docs/project-context.md +52 -0
- package/templates/core/docs/rules/common/coding-style.md +20 -0
- package/templates/core/docs/rules/common/security.md +17 -0
- package/templates/core/docs/rules/common/testing.md +24 -0
- package/templates/core/docs/tasks/README.md +15 -0
- package/templates/entry/AGENTS.md +13 -0
- package/templates/entry/CLAUDE.md +13 -0
- package/templates/manifest.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xudeyiyi
|
|
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,105 @@
|
|
|
1
|
+
# Niuma Harness
|
|
2
|
+
|
|
3
|
+
Initialize an AI engineering harness for a project workspace.
|
|
4
|
+
|
|
5
|
+
Niuma Harness creates a lightweight documentation scaffold that helps AI coding tools understand project context, rules, workflows, and task notes.
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx niuma-harness init . --agent claude
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Interactive mode is available when `--agent` is omitted in a TTY:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx niuma-harness init
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
niuma-harness init [target] [options]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Options
|
|
26
|
+
|
|
27
|
+
| Option | Description |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `--agent <name>` | `claude`, `codex`, `opencode`, or `multi` |
|
|
30
|
+
| `--harness-dir <name>` | Directory to create inside target, default: `harness` |
|
|
31
|
+
| `--rules <mode>` | `copy` or `empty`, default: `copy` |
|
|
32
|
+
| `--flat` | Write directly into target instead of `target/harness` |
|
|
33
|
+
| `--force` | Overwrite existing scaffold files |
|
|
34
|
+
| `--dry-run` | Print planned actions without writing files |
|
|
35
|
+
| `-h`, `--help` | Show help |
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx niuma-harness init . --agent claude
|
|
41
|
+
npx niuma-harness init ./workspace --agent codex --rules empty
|
|
42
|
+
npx niuma-harness init ./workspace --agent opencode --dry-run
|
|
43
|
+
npx niuma-harness init ./workspace --agent multi --harness-dir ai-harness
|
|
44
|
+
npx niuma-harness init ./workspace --agent claude --flat
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Agent modes
|
|
48
|
+
|
|
49
|
+
| Agent | Generated entry file |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `claude` | `CLAUDE.md` |
|
|
52
|
+
| `codex` | `AGENTS.md` |
|
|
53
|
+
| `opencode` | `AGENTS.md` |
|
|
54
|
+
| `multi` | `CLAUDE.md` and `AGENTS.md` |
|
|
55
|
+
|
|
56
|
+
## Generated structure
|
|
57
|
+
|
|
58
|
+
Default output:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
workspace/
|
|
62
|
+
harness/
|
|
63
|
+
CLAUDE.md or AGENTS.md
|
|
64
|
+
HARNESS_GUIDE.md
|
|
65
|
+
docs/
|
|
66
|
+
index.md
|
|
67
|
+
project-context.md
|
|
68
|
+
automation/
|
|
69
|
+
hooks.md
|
|
70
|
+
process/
|
|
71
|
+
task-triage.md
|
|
72
|
+
bugfix.md
|
|
73
|
+
feature-development.md
|
|
74
|
+
rules/
|
|
75
|
+
common/
|
|
76
|
+
coding-style.md
|
|
77
|
+
testing.md
|
|
78
|
+
security.md
|
|
79
|
+
tasks/
|
|
80
|
+
README.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `--flat` to write the same scaffold directly into the target directory instead of creating `harness/`.
|
|
84
|
+
|
|
85
|
+
## Rules mode
|
|
86
|
+
|
|
87
|
+
`--rules copy` is the default and creates starter rule content.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx niuma-harness init . --agent claude --rules copy
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`--rules empty` creates the same rule files with empty content, useful when a team wants to fill its own rules from scratch.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx niuma-harness init . --agent claude --rules empty
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm test
|
|
103
|
+
npm run pack:dry
|
|
104
|
+
node bin/niuma-harness.js init . --agent claude --dry-run
|
|
105
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "niuma-harness",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Initialize an AI engineering harness for a project workspace.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"niuma-harness": "bin/niuma-harness.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node test/cli.test.js",
|
|
18
|
+
"check": "npm test",
|
|
19
|
+
"pack:dry": "npm pack --dry-run"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ai",
|
|
23
|
+
"harness",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"codex",
|
|
26
|
+
"openCode",
|
|
27
|
+
"agent"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/739188210/niuma-harness.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/739188210/niuma-harness/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/739188210/niuma-harness#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"author": "xudeyiyi"
|
|
42
|
+
}
|
package/src/args.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const SUPPORTED_AGENTS = new Set(['claude', 'codex', 'opencode', 'multi']);
|
|
2
|
+
const SUPPORTED_RULES = new Set(['copy', 'empty']);
|
|
3
|
+
|
|
4
|
+
function parseArgs(argv) {
|
|
5
|
+
const options = {
|
|
6
|
+
command: null,
|
|
7
|
+
targetDir: null,
|
|
8
|
+
agent: null,
|
|
9
|
+
rules: 'copy',
|
|
10
|
+
harnessDir: 'harness',
|
|
11
|
+
flat: false,
|
|
12
|
+
force: false,
|
|
13
|
+
dryRun: false,
|
|
14
|
+
help: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
18
|
+
const arg = argv[index];
|
|
19
|
+
|
|
20
|
+
if (arg === '-h' || arg === '--help') {
|
|
21
|
+
options.help = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (arg === '--flat') {
|
|
26
|
+
options.flat = true;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (arg === '--force') {
|
|
31
|
+
options.force = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (arg === '--dry-run') {
|
|
36
|
+
options.dryRun = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (arg === '--agent' || arg === '--rules' || arg === '--harness-dir') {
|
|
41
|
+
const value = argv[index + 1];
|
|
42
|
+
if (!value || value.startsWith('-')) {
|
|
43
|
+
throw new Error(`${arg} requires a value.`);
|
|
44
|
+
}
|
|
45
|
+
assignOption(options, arg.slice(2), value);
|
|
46
|
+
index += 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (arg.startsWith('--agent=') || arg.startsWith('--rules=') || arg.startsWith('--harness-dir=')) {
|
|
51
|
+
const [name, value] = splitLongOption(arg);
|
|
52
|
+
assignOption(options, name, value);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (arg.startsWith('-')) {
|
|
57
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!options.command) {
|
|
61
|
+
options.command = arg;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!options.targetDir) {
|
|
66
|
+
options.targetDir = arg;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
options.agent = normalizeAgent(options.agent);
|
|
74
|
+
options.rules = normalizeRules(options.rules);
|
|
75
|
+
options.harnessDir = normalizeHarnessDir(options.harnessDir);
|
|
76
|
+
|
|
77
|
+
return options;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function assignOption(options, name, value) {
|
|
81
|
+
if (name === 'agent') {
|
|
82
|
+
options.agent = value;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (name === 'rules') {
|
|
87
|
+
options.rules = value;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (name === 'harness-dir') {
|
|
92
|
+
options.harnessDir = value;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new Error(`Unknown option: --${name}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function splitLongOption(arg) {
|
|
100
|
+
const equalsIndex = arg.indexOf('=');
|
|
101
|
+
const name = arg.slice(2, equalsIndex);
|
|
102
|
+
const value = arg.slice(equalsIndex + 1);
|
|
103
|
+
|
|
104
|
+
if (!value) {
|
|
105
|
+
throw new Error(`--${name} requires a value.`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [name, value];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function normalizeAgent(agent) {
|
|
112
|
+
if (!agent) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const value = String(agent).trim().toLowerCase();
|
|
117
|
+
if (SUPPORTED_AGENTS.has(value)) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
throw new Error(`--agent must be one of: claude, codex, opencode, multi. Received: ${agent}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeRules(rules) {
|
|
125
|
+
const value = String(rules || 'copy').trim().toLowerCase();
|
|
126
|
+
if (SUPPORTED_RULES.has(value)) {
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new Error(`--rules must be one of: copy, empty. Received: ${rules}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeHarnessDir(harnessDir) {
|
|
134
|
+
const value = String(harnessDir || 'harness').trim();
|
|
135
|
+
if (!value) {
|
|
136
|
+
throw new Error('--harness-dir cannot be empty.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (value === '.' || value.includes('..') || value.includes('/') || value.includes('\\')) {
|
|
140
|
+
throw new Error('--harness-dir must be a simple directory name.');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!/^[A-Za-z0-9._-]+$/.test(value)) {
|
|
144
|
+
throw new Error('--harness-dir may only contain letters, numbers, dots, underscores, and dashes.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getHelpText() {
|
|
151
|
+
return `Usage:
|
|
152
|
+
niuma-harness init [target] [options]
|
|
153
|
+
|
|
154
|
+
Options:
|
|
155
|
+
--agent <name> claude | codex | opencode | multi
|
|
156
|
+
--harness-dir <name> Directory to create inside target, default: harness
|
|
157
|
+
--rules <mode> copy | empty, default: copy
|
|
158
|
+
--flat Write directly into target instead of target/harness
|
|
159
|
+
--force Overwrite existing scaffold files
|
|
160
|
+
--dry-run Print planned actions without writing files
|
|
161
|
+
-h, --help Show help
|
|
162
|
+
|
|
163
|
+
Examples:
|
|
164
|
+
niuma-harness init . --agent claude
|
|
165
|
+
niuma-harness init D:\\work\\app --agent codex --rules empty
|
|
166
|
+
niuma-harness init . --agent multi --harness-dir ai-harness
|
|
167
|
+
niuma-harness init . --agent opencode --flat --dry-run`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
parseArgs,
|
|
172
|
+
normalizeAgent,
|
|
173
|
+
normalizeRules,
|
|
174
|
+
normalizeHarnessDir,
|
|
175
|
+
getHelpText,
|
|
176
|
+
};
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { parseArgs, getHelpText } = require('./args');
|
|
2
|
+
const { chooseAgent } = require('./prompts');
|
|
3
|
+
const { runInit } = require('./scaffold');
|
|
4
|
+
|
|
5
|
+
async function main(argv) {
|
|
6
|
+
const options = parseArgs(argv);
|
|
7
|
+
|
|
8
|
+
if (options.help || !options.command) {
|
|
9
|
+
console.log(getHelpText());
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (options.command !== 'init') {
|
|
14
|
+
throw new Error(`Unknown command: ${options.command}. Only "init" is supported.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
options.agent = await chooseAgent(options.agent);
|
|
18
|
+
runInit(options);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
main,
|
|
23
|
+
};
|
package/src/prompts.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const { normalizeAgent } = require('./args');
|
|
3
|
+
|
|
4
|
+
function ask(question) {
|
|
5
|
+
const rl = readline.createInterface({
|
|
6
|
+
input: process.stdin,
|
|
7
|
+
output: process.stdout,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
rl.question(question, (answer) => {
|
|
12
|
+
rl.close();
|
|
13
|
+
resolve(answer);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function chooseAgent(agent) {
|
|
19
|
+
const normalized = normalizeAgent(agent);
|
|
20
|
+
if (normalized) {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!process.stdin.isTTY) {
|
|
25
|
+
throw new Error('Missing --agent. Use --agent claude, --agent codex, --agent opencode, or --agent multi.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
while (true) {
|
|
29
|
+
const answer = await ask([
|
|
30
|
+
'Choose AI coding agent:',
|
|
31
|
+
' 1. claude -> generate CLAUDE.md',
|
|
32
|
+
' 2. codex -> generate AGENTS.md',
|
|
33
|
+
' 3. opencode -> generate AGENTS.md',
|
|
34
|
+
' 4. multi -> generate CLAUDE.md and AGENTS.md',
|
|
35
|
+
'Enter claude/codex/opencode/multi or 1/2/3/4: ',
|
|
36
|
+
].join('\n'));
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
return normalizeAgentAlias(answer);
|
|
40
|
+
} catch {
|
|
41
|
+
console.log('Please enter claude, codex, opencode, multi, 1, 2, 3, or 4.');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeAgentAlias(value) {
|
|
47
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
48
|
+
const aliases = {
|
|
49
|
+
1: 'claude',
|
|
50
|
+
2: 'codex',
|
|
51
|
+
3: 'opencode',
|
|
52
|
+
4: 'multi',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return normalizeAgent(aliases[normalized] || normalized);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
ask,
|
|
60
|
+
chooseAgent,
|
|
61
|
+
};
|
package/src/scaffold.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
5
|
+
const TEMPLATE_DIR = path.join(ROOT_DIR, 'templates');
|
|
6
|
+
const MANIFEST_PATH = path.join(TEMPLATE_DIR, 'manifest.json');
|
|
7
|
+
|
|
8
|
+
function runInit(options) {
|
|
9
|
+
const workspaceDir = path.resolve(options.targetDir || '.');
|
|
10
|
+
const targetDir = options.flat ? workspaceDir : path.join(workspaceDir, options.harnessDir);
|
|
11
|
+
const manifest = loadManifest();
|
|
12
|
+
validateManifest(manifest);
|
|
13
|
+
|
|
14
|
+
const variables = {
|
|
15
|
+
ENTRY_FILES: getEntryFilesForAgent(options.agent).join(', '),
|
|
16
|
+
HARNESS_DIR: options.flat ? '.' : options.harnessDir,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
console.log(options.dryRun ? 'DRY RUN: preview scaffold changes' : 'Initializing niuma harness');
|
|
20
|
+
console.log(`Workspace: ${workspaceDir}`);
|
|
21
|
+
console.log(`Target: ${targetDir}`);
|
|
22
|
+
console.log(`Agent: ${options.agent}`);
|
|
23
|
+
console.log(`Rules: ${options.rules}`);
|
|
24
|
+
|
|
25
|
+
printAction(ensureDir(targetDir, options.dryRun), targetDir);
|
|
26
|
+
|
|
27
|
+
for (const directory of manifest.directories) {
|
|
28
|
+
const targetPath = safeResolveInside(targetDir, directory, 'directory target');
|
|
29
|
+
printAction(ensureDir(targetPath, options.dryRun), targetPath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const entryFile of getEntryFilesForAgent(options.agent)) {
|
|
33
|
+
const templatePath = entryFile === 'CLAUDE.md' ? 'entry/CLAUDE.md' : 'entry/AGENTS.md';
|
|
34
|
+
const targetPath = safeResolveInside(targetDir, entryFile, 'entry target');
|
|
35
|
+
const content = renderTemplate(templatePath, { ...variables, ENTRY_FILE: entryFile });
|
|
36
|
+
printAction(writeFile(targetPath, content, options), targetPath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const file of manifest.templateFiles) {
|
|
40
|
+
const targetPath = safeResolveInside(targetDir, file.target, 'template target');
|
|
41
|
+
const content = renderTemplate(file.template, variables);
|
|
42
|
+
printAction(writeFile(targetPath, content, options), targetPath);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const file of manifest.ruleFiles) {
|
|
46
|
+
const targetPath = safeResolveInside(targetDir, file.target, 'rule target');
|
|
47
|
+
const content = options.rules === 'copy' ? renderTemplate(file.template, variables) : '';
|
|
48
|
+
printAction(writeFile(targetPath, content, options), targetPath);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log('Done. Read HARNESS_GUIDE.md and fill docs/index.md for your project.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function loadManifest() {
|
|
55
|
+
return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function validateManifest(manifest) {
|
|
59
|
+
for (const directory of manifest.directories || []) {
|
|
60
|
+
validateRelativePath(directory, 'manifest directory');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const file of [...(manifest.templateFiles || []), ...(manifest.ruleFiles || [])]) {
|
|
64
|
+
validateRelativePath(file.target, 'manifest target');
|
|
65
|
+
validateRelativePath(file.template, 'manifest template');
|
|
66
|
+
safeResolveInside(TEMPLATE_DIR, file.template, 'manifest template');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getEntryFilesForAgent(agent) {
|
|
71
|
+
if (agent === 'claude') {
|
|
72
|
+
return ['CLAUDE.md'];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (agent === 'codex' || agent === 'opencode') {
|
|
76
|
+
return ['AGENTS.md'];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (agent === 'multi') {
|
|
80
|
+
return ['CLAUDE.md', 'AGENTS.md'];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new Error(`Unsupported agent: ${agent}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function readTemplate(relativePath) {
|
|
87
|
+
const templatePath = safeResolveInside(TEMPLATE_DIR, relativePath, 'template path');
|
|
88
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderTemplate(relativePath, variables) {
|
|
92
|
+
let content = readTemplate(relativePath);
|
|
93
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
94
|
+
content = content.split(`{{${key}}}`).join(value);
|
|
95
|
+
}
|
|
96
|
+
return content;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function ensureDir(dirPath, dryRun) {
|
|
100
|
+
assertNoSymlinkInPath(dirPath);
|
|
101
|
+
|
|
102
|
+
if (fs.existsSync(dirPath)) {
|
|
103
|
+
const stat = fs.lstatSync(dirPath);
|
|
104
|
+
if (!stat.isDirectory()) {
|
|
105
|
+
throw new Error(`Path exists but is not a directory: ${dirPath}`);
|
|
106
|
+
}
|
|
107
|
+
return 'skip';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!dryRun) {
|
|
111
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
return 'create';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function writeFile(filePath, content, options) {
|
|
117
|
+
assertNoSymlinkInPath(filePath);
|
|
118
|
+
|
|
119
|
+
const exists = fs.existsSync(filePath);
|
|
120
|
+
if (exists && !options.force) {
|
|
121
|
+
return 'skip';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (exists) {
|
|
125
|
+
const stat = fs.lstatSync(filePath);
|
|
126
|
+
if (!stat.isFile()) {
|
|
127
|
+
throw new Error(`Path exists but is not a regular file: ${filePath}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!options.dryRun) {
|
|
132
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
133
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (exists && options.force) {
|
|
137
|
+
return 'overwrite';
|
|
138
|
+
}
|
|
139
|
+
return 'create';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function validateRelativePath(relativePath, label) {
|
|
143
|
+
if (!relativePath || typeof relativePath !== 'string') {
|
|
144
|
+
throw new Error(`Invalid ${label}: path must be a non-empty string.`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (path.isAbsolute(relativePath)) {
|
|
148
|
+
throw new Error(`Invalid ${label}: absolute paths are not allowed: ${relativePath}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const segments = relativePath.split(/[\\/]+/);
|
|
152
|
+
if (segments.includes('..') || segments.includes('')) {
|
|
153
|
+
throw new Error(`Invalid ${label}: path must stay inside the scaffold: ${relativePath}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function safeResolveInside(baseDir, relativePath, label) {
|
|
158
|
+
validateRelativePath(relativePath, label);
|
|
159
|
+
const base = path.resolve(baseDir);
|
|
160
|
+
const resolved = path.resolve(base, relativePath);
|
|
161
|
+
if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) {
|
|
162
|
+
throw new Error(`Invalid ${label}: path escapes base directory: ${relativePath}`);
|
|
163
|
+
}
|
|
164
|
+
return resolved;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function assertNoSymlinkInPath(targetPath) {
|
|
168
|
+
const resolved = path.resolve(targetPath);
|
|
169
|
+
const root = path.parse(resolved).root;
|
|
170
|
+
const relative = path.relative(root, resolved);
|
|
171
|
+
const parts = relative ? relative.split(path.sep) : [];
|
|
172
|
+
let current = root;
|
|
173
|
+
|
|
174
|
+
for (const part of parts) {
|
|
175
|
+
current = path.join(current, part);
|
|
176
|
+
if (!fs.existsSync(current)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const stat = fs.lstatSync(current);
|
|
181
|
+
if (stat.isSymbolicLink()) {
|
|
182
|
+
throw new Error(`Refusing to write through symlink: ${current}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function printAction(action, targetPath) {
|
|
188
|
+
const label = {
|
|
189
|
+
create: 'CREATE',
|
|
190
|
+
overwrite: 'OVERWRITE',
|
|
191
|
+
skip: 'SKIP',
|
|
192
|
+
}[action] || action.toUpperCase();
|
|
193
|
+
|
|
194
|
+
console.log(`${label.padEnd(9)} ${targetPath}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
runInit,
|
|
199
|
+
loadManifest,
|
|
200
|
+
validateManifest,
|
|
201
|
+
getEntryFilesForAgent,
|
|
202
|
+
renderTemplate,
|
|
203
|
+
ensureDir,
|
|
204
|
+
writeFile,
|
|
205
|
+
safeResolveInside,
|
|
206
|
+
assertNoSymlinkInPath,
|
|
207
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Niuma Harness Guide
|
|
2
|
+
|
|
3
|
+
This directory contains the AI engineering harness for the workspace.
|
|
4
|
+
|
|
5
|
+
Generated entry file(s): `{{ENTRY_FILES}}`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
The harness gives AI coding tools a stable operating context:
|
|
10
|
+
|
|
11
|
+
- where project code and docs live
|
|
12
|
+
- which rules to follow
|
|
13
|
+
- which process to use for common task types
|
|
14
|
+
- where to record task state and verification evidence
|
|
15
|
+
|
|
16
|
+
## First steps
|
|
17
|
+
|
|
18
|
+
1. Fill `docs/index.md` with the real project paths and important docs.
|
|
19
|
+
2. Fill `docs/project-context.md` with stable project facts.
|
|
20
|
+
3. Adjust `docs/rules/` to match your team conventions.
|
|
21
|
+
4. Use `docs/process/` as the default workflow reference.
|
|
22
|
+
5. Store multi-step task notes in `docs/tasks/`.
|
|
23
|
+
|
|
24
|
+
## Directory map
|
|
25
|
+
|
|
26
|
+
- `docs/index.md`: project map and reading order.
|
|
27
|
+
- `docs/project-context.md`: stable project context.
|
|
28
|
+
- `docs/rules/`: coding, testing, and security rules.
|
|
29
|
+
- `docs/process/`: reusable task workflows.
|
|
30
|
+
- `docs/automation/`: automation and hook intent notes.
|
|
31
|
+
- `docs/tasks/`: task records and handoff notes.
|
|
32
|
+
|
|
33
|
+
## Maintenance rule
|
|
34
|
+
|
|
35
|
+
Keep long-lived facts in `docs/project-context.md`. Keep temporary investigation notes in `docs/tasks/` or another task-local location.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Automation and Hook Intent
|
|
2
|
+
|
|
3
|
+
This MVP does not install tool-specific hooks automatically.
|
|
4
|
+
|
|
5
|
+
Use this document to describe automation you may want to map to Claude Code hooks, Codex hooks, opencode plugins, Git hooks, or CI jobs.
|
|
6
|
+
|
|
7
|
+
| Intent | Trigger | Suggested command | Status |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| Format changed files | after file edit | `<fill command>` | planned |
|
|
10
|
+
| Run focused tests | before completion | `<fill command>` | planned |
|
|
11
|
+
| Check secrets | before commit or release | `<fill command>` | planned |
|
|
12
|
+
| Verify build | before release | `<fill command>` | planned |
|
|
13
|
+
|
|
14
|
+
## Maintenance
|
|
15
|
+
|
|
16
|
+
Keep actual hook configuration in the relevant tool-specific location. Keep this file as the shared source for automation intent.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Harness Index
|
|
2
|
+
|
|
3
|
+
Use this file as the map for AI tools working in this workspace.
|
|
4
|
+
|
|
5
|
+
## Code locations
|
|
6
|
+
|
|
7
|
+
- Backend: `<fill path, e.g. ../backend>`
|
|
8
|
+
- Frontend: `<fill path, e.g. ../frontend>`
|
|
9
|
+
- Shared packages: `<fill path or N/A>`
|
|
10
|
+
|
|
11
|
+
## Required reading
|
|
12
|
+
|
|
13
|
+
- Project context: `docs/project-context.md`
|
|
14
|
+
- Task workflows: `docs/process/`
|
|
15
|
+
- Rules: `docs/rules/`
|
|
16
|
+
- Automation notes: `docs/automation/hooks.md`
|
|
17
|
+
|
|
18
|
+
## Task workflows
|
|
19
|
+
|
|
20
|
+
- Task triage: `docs/process/task-triage.md`
|
|
21
|
+
- Bug fixes: `docs/process/bugfix.md`
|
|
22
|
+
- Feature development: `docs/process/feature-development.md`
|
|
23
|
+
|
|
24
|
+
## Verification commands
|
|
25
|
+
|
|
26
|
+
Document project-specific commands here:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# tests
|
|
30
|
+
<fill command>
|
|
31
|
+
|
|
32
|
+
# lint/typecheck/build
|
|
33
|
+
<fill command>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Maintenance
|
|
37
|
+
|
|
38
|
+
Update this index when project paths, important docs, or standard workflows change.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Bugfix Process
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Fix a defect with evidence that the behavior is corrected and existing behavior is not unexpectedly broken.
|
|
6
|
+
|
|
7
|
+
## Steps
|
|
8
|
+
|
|
9
|
+
1. Understand the symptom and expected behavior.
|
|
10
|
+
2. Locate the relevant code path.
|
|
11
|
+
3. Reproduce the issue with a test, command, or manual steps when practical.
|
|
12
|
+
4. Implement the smallest safe fix.
|
|
13
|
+
5. Run focused verification.
|
|
14
|
+
6. Run broader regression checks when the touched area is shared.
|
|
15
|
+
7. Report root cause, changed files, verification result, and residual risk.
|
|
16
|
+
|
|
17
|
+
## Notes
|
|
18
|
+
|
|
19
|
+
If reproduction is not possible in the current environment, document the missing condition and the exact verification steps to run later.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Feature Development Process
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Deliver a feature through clear requirements, implementation notes, and verification evidence.
|
|
6
|
+
|
|
7
|
+
## Lightweight flow
|
|
8
|
+
|
|
9
|
+
1. Clarify the goal and acceptance criteria.
|
|
10
|
+
2. Read project context and relevant existing implementation patterns.
|
|
11
|
+
3. Plan the smallest safe implementation path.
|
|
12
|
+
4. Add or update tests for behavior when practical.
|
|
13
|
+
5. Implement the feature.
|
|
14
|
+
6. Run relevant verification commands.
|
|
15
|
+
7. Record what changed, what passed, what failed, and what remains.
|
|
16
|
+
|
|
17
|
+
## When to pause
|
|
18
|
+
|
|
19
|
+
Pause and ask before:
|
|
20
|
+
|
|
21
|
+
- changing public APIs or data contracts
|
|
22
|
+
- adding dependencies
|
|
23
|
+
- changing authentication, authorization, or security-sensitive behavior
|
|
24
|
+
- making irreversible data changes
|
|
25
|
+
- proceeding when acceptance criteria are unclear
|
|
26
|
+
|
|
27
|
+
## Task notes
|
|
28
|
+
|
|
29
|
+
For multi-step features, create a task folder under `docs/tasks/<task-name>/` and keep context, plan, and verification notes there.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Task Triage Process
|
|
2
|
+
|
|
3
|
+
Use this process before choosing a more specific workflow.
|
|
4
|
+
|
|
5
|
+
## 1. Classify the task
|
|
6
|
+
|
|
7
|
+
- Question or explanation only
|
|
8
|
+
- Small edit
|
|
9
|
+
- Bug fix
|
|
10
|
+
- Feature development
|
|
11
|
+
- Refactor
|
|
12
|
+
- Security-sensitive change
|
|
13
|
+
- Documentation update
|
|
14
|
+
|
|
15
|
+
## 2. Identify required context
|
|
16
|
+
|
|
17
|
+
- Read `docs/index.md`.
|
|
18
|
+
- Read `docs/project-context.md` when design or code changes are involved.
|
|
19
|
+
- Read relevant rules under `docs/rules/`.
|
|
20
|
+
|
|
21
|
+
## 3. Choose workflow
|
|
22
|
+
|
|
23
|
+
- Bug fix: `docs/process/bugfix.md`
|
|
24
|
+
- Feature development: `docs/process/feature-development.md`
|
|
25
|
+
- Other tasks: use the closest workflow and keep notes in `docs/tasks/` when needed.
|
|
26
|
+
|
|
27
|
+
## 4. Confirm blockers
|
|
28
|
+
|
|
29
|
+
Ask for clarification when the task changes public contracts, adds dependencies, touches security-sensitive behavior, or lacks enough acceptance criteria.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Project Context
|
|
2
|
+
|
|
3
|
+
This file stores stable facts about the project. Do not use it for temporary investigation notes.
|
|
4
|
+
|
|
5
|
+
## Metadata
|
|
6
|
+
|
|
7
|
+
- Created:
|
|
8
|
+
- Last updated:
|
|
9
|
+
- Scan scope:
|
|
10
|
+
- Known gaps:
|
|
11
|
+
|
|
12
|
+
## Project summary
|
|
13
|
+
|
|
14
|
+
Describe the product, main users, and primary business/domain goals.
|
|
15
|
+
|
|
16
|
+
## Technology stack
|
|
17
|
+
|
|
18
|
+
- Runtime/language:
|
|
19
|
+
- Frameworks:
|
|
20
|
+
- Package manager:
|
|
21
|
+
- Database/storage:
|
|
22
|
+
- Test framework:
|
|
23
|
+
|
|
24
|
+
## Module structure
|
|
25
|
+
|
|
26
|
+
Document stable module boundaries and important paths.
|
|
27
|
+
|
|
28
|
+
## Engineering conventions
|
|
29
|
+
|
|
30
|
+
- API response format:
|
|
31
|
+
- Error handling:
|
|
32
|
+
- Auth/security:
|
|
33
|
+
- Logging:
|
|
34
|
+
- Configuration:
|
|
35
|
+
|
|
36
|
+
## Build and verification commands
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# install
|
|
40
|
+
|
|
41
|
+
# test
|
|
42
|
+
|
|
43
|
+
# lint/typecheck/build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Reference implementations
|
|
47
|
+
|
|
48
|
+
List stable reference modules or features that agents should reuse as patterns.
|
|
49
|
+
|
|
50
|
+
## Open questions
|
|
51
|
+
|
|
52
|
+
Track project-level questions that cannot be answered from current files.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Coding Style Rules
|
|
2
|
+
|
|
3
|
+
## Principles
|
|
4
|
+
|
|
5
|
+
- Prefer simple, readable code over clever abstractions.
|
|
6
|
+
- Match the surrounding project style.
|
|
7
|
+
- Make focused changes; do not refactor unrelated code.
|
|
8
|
+
- Reuse existing utilities and patterns before adding new ones.
|
|
9
|
+
|
|
10
|
+
## Naming
|
|
11
|
+
|
|
12
|
+
- Use descriptive names.
|
|
13
|
+
- Keep files and modules cohesive.
|
|
14
|
+
- Avoid ambiguous abbreviations unless already common in the project.
|
|
15
|
+
|
|
16
|
+
## Change discipline
|
|
17
|
+
|
|
18
|
+
- Do not introduce new dependencies without explicit approval.
|
|
19
|
+
- Do not change public contracts unless the task requires it and the risk is called out.
|
|
20
|
+
- Remove only dead code introduced by the current change unless asked otherwise.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Security Rules
|
|
2
|
+
|
|
3
|
+
## Secrets
|
|
4
|
+
|
|
5
|
+
- Never hardcode credentials, tokens, API keys, or private endpoints.
|
|
6
|
+
- Use environment variables or the project's existing secret management pattern.
|
|
7
|
+
|
|
8
|
+
## Inputs and outputs
|
|
9
|
+
|
|
10
|
+
- Validate input at system boundaries.
|
|
11
|
+
- Avoid unsafe shell execution and path handling.
|
|
12
|
+
- Avoid rendering or logging sensitive data.
|
|
13
|
+
|
|
14
|
+
## Risk handling
|
|
15
|
+
|
|
16
|
+
Stop and ask before making security-sensitive changes when requirements are unclear.
|
|
17
|
+
Document residual security risk in task notes or verification output.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Testing Rules
|
|
2
|
+
|
|
3
|
+
## Default expectation
|
|
4
|
+
|
|
5
|
+
Changes should be verified before being called complete.
|
|
6
|
+
|
|
7
|
+
## Test-first preference
|
|
8
|
+
|
|
9
|
+
For behavior changes:
|
|
10
|
+
|
|
11
|
+
1. Reproduce the current behavior or failure.
|
|
12
|
+
2. Add or update a focused test when practical.
|
|
13
|
+
3. Implement the smallest fix.
|
|
14
|
+
4. Run the relevant verification commands.
|
|
15
|
+
|
|
16
|
+
## Reporting
|
|
17
|
+
|
|
18
|
+
Always report:
|
|
19
|
+
|
|
20
|
+
- commands run
|
|
21
|
+
- whether they passed or failed
|
|
22
|
+
- important skipped checks and why
|
|
23
|
+
|
|
24
|
+
Do not claim completion when relevant tests fail unless the user explicitly accepts the remaining risk.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Task Notes
|
|
2
|
+
|
|
3
|
+
Use this directory for multi-step task records, handoffs, and verification notes.
|
|
4
|
+
|
|
5
|
+
Suggested structure:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
docs/tasks/<task-name>/
|
|
9
|
+
context.md
|
|
10
|
+
plan.md
|
|
11
|
+
verification.md
|
|
12
|
+
notes.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Keep long-lived project facts in `docs/project-context.md`, not here.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Agent Entry
|
|
2
|
+
|
|
3
|
+
This workspace uses a Niuma Harness documentation scaffold.
|
|
4
|
+
|
|
5
|
+
Before starting work:
|
|
6
|
+
|
|
7
|
+
1. Read `docs/index.md` to understand the project map.
|
|
8
|
+
2. Read `docs/project-context.md` before design or code changes.
|
|
9
|
+
3. Follow the relevant process under `docs/process/`.
|
|
10
|
+
4. Follow shared rules under `docs/rules/`.
|
|
11
|
+
5. Record task notes under `docs/tasks/` when work spans multiple steps.
|
|
12
|
+
|
|
13
|
+
This file is the generic agent entry point for Codex, opencode, and compatible tools. Treat `docs/` as the source of truth for project context, process, and rules.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Claude Code Entry
|
|
2
|
+
|
|
3
|
+
This workspace uses a Niuma Harness documentation scaffold.
|
|
4
|
+
|
|
5
|
+
Before starting work:
|
|
6
|
+
|
|
7
|
+
1. Read `docs/index.md` to understand the project map.
|
|
8
|
+
2. Read `docs/project-context.md` before design or code changes.
|
|
9
|
+
3. Follow the relevant process under `docs/process/`.
|
|
10
|
+
4. Follow shared rules under `docs/rules/`.
|
|
11
|
+
5. Record task notes under `docs/tasks/` when work spans multiple steps.
|
|
12
|
+
|
|
13
|
+
This file is only the Claude Code entry point. Treat `docs/` as the source of truth for project context, process, and rules.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"directories": [
|
|
3
|
+
"docs",
|
|
4
|
+
"docs/automation",
|
|
5
|
+
"docs/process",
|
|
6
|
+
"docs/rules",
|
|
7
|
+
"docs/rules/common",
|
|
8
|
+
"docs/tasks"
|
|
9
|
+
],
|
|
10
|
+
"templateFiles": [
|
|
11
|
+
{
|
|
12
|
+
"target": "HARNESS_GUIDE.md",
|
|
13
|
+
"template": "core/HARNESS_GUIDE.md"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"target": "docs/index.md",
|
|
17
|
+
"template": "core/docs/index.md"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"target": "docs/project-context.md",
|
|
21
|
+
"template": "core/docs/project-context.md"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"target": "docs/process/task-triage.md",
|
|
25
|
+
"template": "core/docs/process/task-triage.md"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"target": "docs/process/bugfix.md",
|
|
29
|
+
"template": "core/docs/process/bugfix.md"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"target": "docs/process/feature-development.md",
|
|
33
|
+
"template": "core/docs/process/feature-development.md"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"target": "docs/automation/hooks.md",
|
|
37
|
+
"template": "core/docs/automation/hooks.md"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"target": "docs/tasks/README.md",
|
|
41
|
+
"template": "core/docs/tasks/README.md"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"ruleFiles": [
|
|
45
|
+
{
|
|
46
|
+
"target": "docs/rules/common/coding-style.md",
|
|
47
|
+
"template": "core/docs/rules/common/coding-style.md"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"target": "docs/rules/common/testing.md",
|
|
51
|
+
"template": "core/docs/rules/common/testing.md"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"target": "docs/rules/common/security.md",
|
|
55
|
+
"template": "core/docs/rules/common/security.md"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|