@plainconceptsplatform/agent-harness 3.8.1 → 3.8.2
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/cli/steps/copy/agents.js
CHANGED
|
@@ -2,7 +2,7 @@ import fse from 'fs-extra'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { fileURLToPath } from 'url'
|
|
4
4
|
import { info, success, warn } from '../../utils/exec.js'
|
|
5
|
-
import { ContractError, loadContract } from '../../../harness/.opencode/scripts/platform-contract.mjs'
|
|
5
|
+
import { ContractError, isTemplateSource, loadContract } from '../../../harness/.opencode/scripts/platform-contract.mjs'
|
|
6
6
|
|
|
7
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
8
8
|
const agentsContent = await fse.readJson(path.resolve(__dirname, '../../presets/agents-content.json'))
|
|
@@ -74,7 +74,7 @@ function replaceBetween(content, start, end, replacement) {
|
|
|
74
74
|
return content.replace(pattern, () => (body ? `${start}\n${body}\n${end}` : `${start}\n${end}`))
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export function reuseBlock(contract) {
|
|
77
|
+
export function reuseBlock(contract, templateSource = false) {
|
|
78
78
|
const { docs = [], packages = [], names = [] } = contract?.reuse ?? {}
|
|
79
79
|
if (!docs.length && !packages.length && !names.length) return ''
|
|
80
80
|
const code = list => list.map(item => `\`${item}\``).join(', ')
|
|
@@ -86,13 +86,14 @@ export function reuseBlock(contract) {
|
|
|
86
86
|
...(docs.length ? [`- Never design something new before reading ${code(docs)}.`] : []),
|
|
87
87
|
...(packages.length ? [`- Never re-implement an export of ${code(packages)}; import it.`] : []),
|
|
88
88
|
...(names.length ? [`- Never write your own ${code(names)}; reuse the existing one.`] : []),
|
|
89
|
-
`- Never edit a path listed under \`owned\`. Ask for the change at ${contract.source.changes}, or add a dated exception to \`${contract.exceptions}
|
|
89
|
+
...(templateSource ? [] : [`- Never edit a path listed under \`owned\`. Ask for the change at ${contract.source.changes}, or add a dated exception to \`${contract.exceptions}\`.`]),
|
|
90
90
|
].join('\n')
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function contractBlock(cwd) {
|
|
94
94
|
try {
|
|
95
|
-
|
|
95
|
+
const contract = loadContract(cwd)
|
|
96
|
+
return reuseBlock(contract, isTemplateSource(cwd, contract))
|
|
96
97
|
} catch (err) {
|
|
97
98
|
if (!(err instanceof ContractError)) throw err
|
|
98
99
|
warn(`${err.message} The "Reuse before you build" block in AGENTS.md was left as it was.`)
|
|
@@ -18,7 +18,7 @@ import { execFile } from "node:child_process"
|
|
|
18
18
|
import fs from "node:fs/promises"
|
|
19
19
|
import path from "node:path"
|
|
20
20
|
import { promisify } from "node:util"
|
|
21
|
-
import { ContractError, describeOwner, isOwned, loadContract } from "../scripts/platform-contract.mjs"
|
|
21
|
+
import { ContractError, describeOwner, isOwned, isTemplateSource, loadContract } from "../scripts/platform-contract.mjs"
|
|
22
22
|
|
|
23
23
|
const GUARDRAILS_SKILL = "pc-guardrails-generic"
|
|
24
24
|
const TIER_SUFFIX = /\.(?:build|fast|plan)$/
|
|
@@ -210,7 +210,7 @@ async function checkPlatform(event, root) {
|
|
|
210
210
|
return deny(event, error.message, "Fix that file before editing anything else: until it reads, nothing can tell which files the template owns.")
|
|
211
211
|
}
|
|
212
212
|
const owned = contract ? event.resources.filter(target => isOwnedTarget(target, root, contract)) : []
|
|
213
|
-
if (!owned.length || await syncInProgress(root, contract.sync.marker)) return
|
|
213
|
+
if (!owned.length || isTemplateSource(root, contract) || await syncInProgress(root, contract.sync.marker)) return
|
|
214
214
|
const { owner, fix } = describeOwner(contract)
|
|
215
215
|
return deny(event, `\`${owned[0]}\` is ${owner}.`, fix)
|
|
216
216
|
}
|
|
@@ -3,7 +3,7 @@ import fs from 'node:fs'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
5
5
|
import { commentBlocks, countLines, isGenerated, kindOf, lineKinds, slopFindings, testStats } from './code-rules.mjs'
|
|
6
|
-
import { ContractError, describeOwner, isOwned, loadContract, nameKey, reusableNames } from './platform-contract.mjs'
|
|
6
|
+
import { ContractError, describeOwner, isOwned, isTemplateSource, loadContract, nameKey, reusableNames } from './platform-contract.mjs'
|
|
7
7
|
|
|
8
8
|
export const DEFAULT_LIMITS = {
|
|
9
9
|
commentRatio: 0.1,
|
|
@@ -213,6 +213,7 @@ export function trackedFiles(root, baseline) {
|
|
|
213
213
|
|
|
214
214
|
// A diff that changes the sync marker is a template sync, the one change allowed to rewrite owned files.
|
|
215
215
|
export function platformViolations(root, base, paths, contract) {
|
|
216
|
+
if (isTemplateSource(root, contract)) return []
|
|
216
217
|
const touched = new Set(untrackedFiles(root))
|
|
217
218
|
for (const { status, from, to } of changes(root, base)) {
|
|
218
219
|
touched.add(to)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process'
|
|
1
2
|
import fs from 'node:fs'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
|
|
@@ -268,6 +269,32 @@ export function loadContract(root) {
|
|
|
268
269
|
return { ...contract, rules: toRules(contract.owned), exceptionList: Array.isArray(entries) ? entries.filter(isMapping) : [] }
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
export function normalizeRepoUrl(url) {
|
|
273
|
+
return String(url ?? '')
|
|
274
|
+
.trim()
|
|
275
|
+
.replace(/^git\+/, '')
|
|
276
|
+
.replace(/^gh:/, 'https://github.com/')
|
|
277
|
+
.replace(/^ssh:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\//, 'https://$1/')
|
|
278
|
+
.replace(/^git@([^:]+):/, 'https://$1/')
|
|
279
|
+
.replace(/^(https?:\/\/)[^@/]+@/, '$1')
|
|
280
|
+
.replace(/^http:\/\//, 'https://')
|
|
281
|
+
.replace(/\/+$/, '')
|
|
282
|
+
.replace(/\.git$/, '')
|
|
283
|
+
.toLowerCase()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// The template repository carries its own contract, and its owned paths are its own to change.
|
|
287
|
+
export function isTemplateSource(root, contract) {
|
|
288
|
+
if (!contract?.source.url) return false
|
|
289
|
+
let origin
|
|
290
|
+
try {
|
|
291
|
+
origin = execFileSync('git', ['config', '--get', 'remote.origin.url'], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
|
|
292
|
+
} catch {
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
return normalizeRepoUrl(origin) !== '' && normalizeRepoUrl(origin) === normalizeRepoUrl(contract.source.url)
|
|
296
|
+
}
|
|
297
|
+
|
|
271
298
|
const localDate = (now = new Date()) =>
|
|
272
299
|
`${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
|
273
300
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plainconceptsplatform/agent-harness",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.2",
|
|
4
4
|
"description": "Installs the Plain Concepts Platform Harness into any codebase, and keeps it up to date. Wires OpenCode, OpenSpec, codegraph, and agentmemory into a multi-agent workflow that runs on native parallel subagents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|